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

Last change on this file since 3555 was 3555, checked in by mervart, 12 years ago
File size: 31.1 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 }
[3553]259 QListIterator<unsigned long> itTime(_buffer.keys());
[3551]260 while (itTime.hasNext()) {
261 _buffer.remove(itTime.next());
[3429]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 // -------------------------
[3553]341 QListIterator<unsigned long> itTime(_buffer.keys());
[3435]342 while (itTime.hasNext()) {
[3553]343 unsigned long epoTime = itTime.next();
344 if (epoTime < (newCorr->tt - moduloTime).longSec()) {
345 _resTime = _buffer[epoTime].tt();
[3435]346 processEpoch();
347 }
[2927]348 }
349
[3429]350 // Merge or add the correction
351 // ---------------------------
[3553]352 QVector<cmbCorr*>& corrs = _buffer[newCorr->tt.longSec()].corrs;
[3429]353 cmbCorr* existingCorr = 0;
[3435]354 QVectorIterator<cmbCorr*> itCorr(corrs);
[3429]355 while (itCorr.hasNext()) {
356 cmbCorr* hlp = itCorr.next();
[3554]357 if (hlp->prn == newCorr->prn && hlp->acName == newCorr->acName) {
[3429]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 }
[3555]369
370 if (_resTime.valid()) {
371 QListIterator<unsigned long> it(_buffer.keys());
372 while (it.hasNext()) {
373 unsigned long epoTime = it.next();
374 if (epoTime <= _resTime.longSec()) {
375 _buffer.remove(epoTime);
376 }
377 }
378 }
[2898]379}
380
[2986]381// Change the correction so that it refers to last received ephemeris
382////////////////////////////////////////////////////////////////////////////
[3029]383void bncComb::switchToLastEph(const t_eph* lastEph, t_corr* corr) {
[3028]384
[3429]385 if (corr->eph == lastEph) {
386 return;
387 }
388
[2987]389 ColumnVector oldXC(4);
390 ColumnVector oldVV(3);
[3029]391 corr->eph->position(corr->tt.gpsw(), corr->tt.gpssec(),
392 oldXC.data(), oldVV.data());
[2988]393
[2987]394 ColumnVector newXC(4);
395 ColumnVector newVV(3);
[3029]396 lastEph->position(corr->tt.gpsw(), corr->tt.gpssec(),
[2987]397 newXC.data(), newVV.data());
[2988]398
399 ColumnVector dX = newXC.Rows(1,3) - oldXC.Rows(1,3);
[2989]400 ColumnVector dV = newVV - oldVV;
401 double dC = newXC(4) - oldXC(4);
402
[2988]403 ColumnVector dRAO(3);
404 XYZ_to_RSW(newXC.Rows(1,3), newVV, dX, dRAO);
[2989]405
406 ColumnVector dDotRAO(3);
407 XYZ_to_RSW(newXC.Rows(1,3), newVV, dV, dDotRAO);
408
[3455]409 QString msg = "switch corr " + corr->prn
[3029]410 + QString(" %1 -> %2 %3").arg(corr->iod,3)
[3013]411 .arg(lastEph->IOD(),3).arg(dC*t_CST::c, 8, 'f', 4);
412
[3029]413 emit newMessage(msg.toAscii(), false);
[3028]414
[3029]415 corr->iod = lastEph->IOD();
416 corr->eph = lastEph;
[3031]417 corr->rao += dRAO;
418 corr->dotRao += dDotRAO;
[3029]419 corr->dClk -= dC;
[2986]420}
421
[3429]422// Process Epoch
[2928]423////////////////////////////////////////////////////////////////////////////
[3429]424void bncComb::processEpoch() {
[2918]425
[2990]426 _log.clear();
427
428 QTextStream out(&_log, QIODevice::WriteOnly);
429
[3472]430 out << endl << "Combination:" << endl
431 << "------------------------------" << endl;
[2990]432
[3433]433 // Observation Statistics
434 // ----------------------
[3455]435 bool masterPresent = false;
[3433]436 QListIterator<cmbAC*> icAC(_ACs);
437 while (icAC.hasNext()) {
438 cmbAC* AC = icAC.next();
439 AC->numObs = 0;
[3434]440 QVectorIterator<cmbCorr*> itCorr(corrs());
[3433]441 while (itCorr.hasNext()) {
442 cmbCorr* corr = itCorr.next();
443 if (corr->acName == AC->name) {
444 AC->numObs += 1;
[3453]445 if (AC->name == _masterOrbitAC) {
446 masterPresent = true;
447 }
[3433]448 }
449 }
450 out << AC->name.toAscii().data() << ": " << AC->numObs << endl;
451 }
452
[3453]453 // If Master not present, switch to another one
454 // --------------------------------------------
[3456]455 if (masterPresent) {
456 _masterMissingEpochs = 0;
457 }
458 else {
[3455]459 ++_masterMissingEpochs;
[3459]460 if (_masterMissingEpochs < 10) {
[3457]461 out << "Missing Master, Epoch skipped" << endl;
[3553]462 _buffer.remove(_resTime.longSec());
[3455]463 emit newMessage(_log, false);
464 return;
[3453]465 }
[3455]466 else {
467 _masterMissingEpochs = 0;
468 QListIterator<cmbAC*> icAC(_ACs);
469 while (icAC.hasNext()) {
470 cmbAC* AC = icAC.next();
471 if (AC->numObs > 0) {
472 out << "Switching Master AC "
473 << _masterOrbitAC.toAscii().data() << " --> "
474 << AC->name.toAscii().data() << " "
475 << _resTime.datestr().c_str() << " "
476 << _resTime.timestr().c_str() << endl;
477 _masterOrbitAC = AC->name;
478 break;
479 }
[3452]480 }
481 }
482 }
483
[3472]484 QMap<QString, t_corr*> resCorr;
485
486 // Perform the actual Combination using selected Method
487 // ----------------------------------------------------
488 t_irc irc;
[3475]489 ColumnVector dx;
[3472]490 if (_method == filter) {
[3475]491 irc = processEpoch_filter(out, resCorr, dx);
[3472]492 }
493 else {
[3475]494 irc = processEpoch_singleEpoch(out, resCorr, dx);
[3472]495 }
496
[3475]497 // Update Parameter Values, Print Results
498 // --------------------------------------
[3472]499 if (irc == success) {
[3475]500 for (int iPar = 1; iPar <= _params.size(); iPar++) {
501 cmbParam* pp = _params[iPar-1];
502 pp->xx += dx(iPar);
503 if (pp->type == cmbParam::clkSat) {
504 if (resCorr.find(pp->prn) != resCorr.end()) {
505 resCorr[pp->prn]->dClk = pp->xx / t_CST::c;
506 }
507 }
508 out << _resTime.datestr().c_str() << " "
509 << _resTime.timestr().c_str() << " ";
510 out.setRealNumberNotation(QTextStream::FixedNotation);
511 out.setFieldWidth(8);
512 out.setRealNumberPrecision(4);
513 out << pp->toString() << " "
514 << pp->xx << " +- " << sqrt(_QQ(pp->index,pp->index)) << endl;
515 out.setFieldWidth(0);
516 }
[3472]517 printResults(out, resCorr);
518 dumpResults(resCorr);
519 }
520
521 // Delete Data, emit Message
522 // -------------------------
[3553]523 _buffer.remove(_resTime.longSec());
[3472]524 emit newMessage(_log, false);
525}
526
527// Process Epoch - Filter Method
528////////////////////////////////////////////////////////////////////////////
529t_irc bncComb::processEpoch_filter(QTextStream& out,
[3475]530 QMap<QString, t_corr*>& resCorr,
531 ColumnVector& dx) {
[3472]532
[3429]533 // Prediction Step
534 // ---------------
[3472]535 int nPar = _params.size();
[2933]536 ColumnVector x0(nPar);
537 for (int iPar = 1; iPar <= _params.size(); iPar++) {
[3455]538 cmbParam* pp = _params[iPar-1];
539 if (pp->epoSpec) {
[3451]540 pp->xx = 0.0;
[3455]541 _QQ.Row(iPar) = 0.0;
542 _QQ.Column(iPar) = 0.0;
543 _QQ(iPar,iPar) = pp->sig0 * pp->sig0;
[3451]544 }
[3455]545 else {
546 _QQ(iPar,iPar) += pp->sigP * pp->sigP;
547 }
[2933]548 x0(iPar) = pp->xx;
549 }
550
[3487]551 // Check Satellite Positions for Outliers
552 // --------------------------------------
[3497]553 if (checkOrbits(out) != success) {
[3487]554 return failure;
555 }
[3441]556
[3455]557 // Update and outlier detection loop
558 // ---------------------------------
[3487]559 SymmetricMatrix QQ_sav = _QQ;
[3455]560 while (true) {
[3135]561
[3455]562 Matrix AA;
563 ColumnVector ll;
564 DiagonalMatrix PP;
[3429]565
[3455]566 if (createAmat(AA, ll, PP, x0, resCorr) != success) {
[3472]567 return failure;
[3441]568 }
[2933]569
[3429]570 bncModel::kalman(AA, ll, PP, _QQ, dx);
571 ColumnVector vv = ll - AA * dx;
[3080]572
[3429]573 int maxResIndex;
574 double maxRes = vv.maximum_absolute_value1(maxResIndex);
575 out.setRealNumberNotation(QTextStream::FixedNotation);
576 out.setRealNumberPrecision(3);
577 out << _resTime.datestr().c_str() << " " << _resTime.timestr().c_str()
[3432]578 << " Maximum Residuum " << maxRes << ' '
[3434]579 << corrs()[maxResIndex-1]->acName << ' ' << corrs()[maxResIndex-1]->prn;
[3080]580
[3432]581 if (maxRes > _MAXRES) {
582 for (int iPar = 1; iPar <= _params.size(); iPar++) {
583 cmbParam* pp = _params[iPar-1];
584 if (pp->type == cmbParam::offACSat &&
[3434]585 pp->AC == corrs()[maxResIndex-1]->acName &&
586 pp->prn == corrs()[maxResIndex-1]->prn) {
[3432]587 QQ_sav.Row(iPar) = 0.0;
588 QQ_sav.Column(iPar) = 0.0;
[3455]589 QQ_sav(iPar,iPar) = pp->sig0 * pp->sig0;
[3432]590 }
591 }
592
593 out << " Outlier" << endl;
594 _QQ = QQ_sav;
[3455]595 corrs().remove(maxResIndex-1);
[3432]596 }
597 else {
598 out << " OK" << endl;
599 break;
600 }
601 }
602
[3472]603 return success;
[2918]604}
[3011]605
[3201]606// Print results
[3011]607////////////////////////////////////////////////////////////////////////////
[3429]608void bncComb::printResults(QTextStream& out,
[3011]609 const QMap<QString, t_corr*>& resCorr) {
610
611 QMapIterator<QString, t_corr*> it(resCorr);
612 while (it.hasNext()) {
613 it.next();
614 t_corr* corr = it.value();
[3029]615 const t_eph* eph = corr->eph;
[3015]616 if (eph) {
617 double xx, yy, zz, cc;
[3429]618 eph->position(_resTime.gpsw(), _resTime.gpssec(), xx, yy, zz, cc);
[3015]619
[3429]620 out << _resTime.datestr().c_str() << " "
621 << _resTime.timestr().c_str() << " ";
[3015]622 out.setFieldWidth(3);
623 out << "Full Clock " << corr->prn << " " << corr->iod << " ";
624 out.setFieldWidth(14);
625 out << (cc + corr->dClk) * t_CST::c << endl;
[3370]626 out.setFieldWidth(0);
[3015]627 }
628 else {
629 out << "bncComb::printResuls bug" << endl;
630 }
[3011]631 }
632}
[3202]633
634// Send results to RTNet Decoder and directly to PPP Client
635////////////////////////////////////////////////////////////////////////////
[3429]636void bncComb::dumpResults(const QMap<QString, t_corr*>& resCorr) {
[3202]637
[3214]638 ostringstream out; out.setf(std::ios::fixed);
[3216]639 QStringList corrLines;
[3214]640
641 unsigned year, month, day, hour, minute;
642 double sec;
[3429]643 _resTime.civil_date(year, month, day);
644 _resTime.civil_time(hour, minute, sec);
[3214]645
646 out << "* "
647 << setw(4) << year << " "
648 << setw(2) << month << " "
649 << setw(2) << day << " "
650 << setw(2) << hour << " "
651 << setw(2) << minute << " "
652 << setw(12) << setprecision(8) << sec << " "
653 << endl;
654
[3202]655 QMapIterator<QString, t_corr*> it(resCorr);
656 while (it.hasNext()) {
657 it.next();
658 t_corr* corr = it.value();
659
[3214]660 double dT = 60.0;
[3202]661
[3214]662 for (int iTime = 1; iTime <= 2; iTime++) {
663
[3429]664 bncTime time12 = (iTime == 1) ? _resTime : _resTime + dT;
[3214]665
666 ColumnVector xc(4);
667 ColumnVector vv(3);
668 corr->eph->position(time12.gpsw(), time12.gpssec(),
669 xc.data(), vv.data());
[3521]670 bncPPPclient::applyCorr(time12, corr, xc, vv);
[3214]671
672 // Relativistic Correction
673 // -----------------------
674 double relCorr = - 2.0 * DotProduct(xc.Rows(1,3),vv) / t_CST::c / t_CST::c;
675 xc(4) -= relCorr;
676
677 // Code Biases
678 // -----------
679 double dcbP1C1 = 0.0;
680 double dcbP1P2 = 0.0;
681
682 // Correction Phase Center --> CoM
683 // -------------------------------
684 ColumnVector dx(3); dx = 0.0;
685 if (_antex) {
686 double Mjd = time12.mjd() + time12.daysec()/86400.0;
687 if (_antex->satCoMcorrection(corr->prn, Mjd, xc.Rows(1,3), dx) == success) {
688 xc(1) -= dx(1);
689 xc(2) -= dx(2);
690 xc(3) -= dx(3);
691 }
692 else {
693 cout << "antenna not found" << endl;
694 }
695 }
696
697 if (iTime == 1) {
698 out << 'P' << corr->prn.toAscii().data()
699 << setw(14) << setprecision(6) << xc(1) / 1000.0
700 << setw(14) << setprecision(6) << xc(2) / 1000.0
701 << setw(14) << setprecision(6) << xc(3) / 1000.0
702 << setw(14) << setprecision(6) << xc(4) * 1e6
703 << setw(14) << setprecision(6) << relCorr * 1e6
704 << setw(8) << setprecision(3) << dx(1)
705 << setw(8) << setprecision(3) << dx(2)
706 << setw(8) << setprecision(3) << dx(3)
707 << setw(8) << setprecision(3) << dcbP1C1
708 << setw(8) << setprecision(3) << dcbP1P2
709 << setw(6) << setprecision(1) << dT;
[3216]710
[3218]711 QString line;
[3217]712 int messageType = COTYPE_GPSCOMBINED;
[3218]713 int updateInt = 0;
714 line.sprintf("%d %d %d %.1f %s"
715 " %3d"
716 " %8.3f %8.3f %8.3f %8.3f"
717 " %10.5f %10.5f %10.5f %10.5f"
[3262]718 " %10.5f %10.5f %10.5f %10.5f INTERNAL",
[3218]719 messageType, updateInt, time12.gpsw(), time12.gpssec(),
720 corr->prn.toAscii().data(),
721 corr->iod,
[3219]722 corr->dClk * t_CST::c,
[3218]723 corr->rao[0],
724 corr->rao[1],
725 corr->rao[2],
[3219]726 corr->dotDClk * t_CST::c,
[3218]727 corr->dotRao[0],
728 corr->dotRao[1],
729 corr->dotRao[2],
[3262]730 corr->dotDotDClk * t_CST::c,
731 corr->dotDotRao[0],
732 corr->dotDotRao[1],
733 corr->dotDotRao[2]);
[3220]734 corrLines << line;
[3214]735 }
736 else {
737 out << setw(14) << setprecision(6) << xc(1) / 1000.0
738 << setw(14) << setprecision(6) << xc(2) / 1000.0
739 << setw(14) << setprecision(6) << xc(3) / 1000.0 << endl;
740 }
741 }
742
743 delete corr;
744 }
[3228]745 out << "EOE" << endl; // End Of Epoch flag
[3214]746
[3215]747 if (!_rtnetDecoder) {
748 _rtnetDecoder = new bncRtnetDecoder();
749 }
[3221]750
[3215]751 vector<string> errmsg;
[3221]752 _rtnetDecoder->Decode((char*) out.str().data(), out.str().size(), errmsg);
[3215]753
[3216]754 // Optionally send new Corrections to PPP
755 // --------------------------------------
756 bncApp* app = (bncApp*) qApp;
757 if (app->_bncPPPclient) {
758 app->_bncPPPclient->slotNewCorrections(corrLines);
759 }
[3202]760}
[3455]761
762// Create First Design Matrix and Vector of Measurements
763////////////////////////////////////////////////////////////////////////////
764t_irc bncComb::createAmat(Matrix& AA, ColumnVector& ll, DiagonalMatrix& PP,
765 const ColumnVector& x0,
766 QMap<QString, t_corr*>& resCorr) {
767
768 unsigned nPar = _params.size();
769 unsigned nObs = corrs().size();
770
771 if (nObs == 0) {
772 return failure;
773 }
774
[3483]775 int MAXPRN = MAXPRN_GPS;
[3486]776// if (_useGlonass) {
777// MAXPRN = MAXPRN_GPS + MAXPRN_GLONASS;
778// }
[3455]779
[3483]780 const int nCon = (_method == filter) ? 1 + MAXPRN : 0;
781
[3455]782 AA.ReSize(nObs+nCon, nPar); AA = 0.0;
783 ll.ReSize(nObs+nCon); ll = 0.0;
784 PP.ReSize(nObs+nCon); PP = 1.0 / (sigObs * sigObs);
785
786 int iObs = 0;
787
788 QVectorIterator<cmbCorr*> itCorr(corrs());
789 while (itCorr.hasNext()) {
790 cmbCorr* corr = itCorr.next();
791 QString prn = corr->prn;
[3487]792
[3455]793 ++iObs;
794
[3476]795 if (corr->acName == _masterOrbitAC && resCorr.find(prn) == resCorr.end()) {
796 resCorr[prn] = new t_corr(*corr);
[3455]797 }
798
799 for (int iPar = 1; iPar <= _params.size(); iPar++) {
800 cmbParam* pp = _params[iPar-1];
801 AA(iObs, iPar) = pp->partial(corr->acName, prn);
802 }
803
804 ll(iObs) = corr->dClk * t_CST::c - DotProduct(AA.Row(iObs), x0);
805 }
806
807 // Regularization
808 // --------------
[3474]809 if (_method == filter) {
810 const double Ph = 1.e6;
811 PP(nObs+1) = Ph;
[3461]812 for (int iPar = 1; iPar <= _params.size(); iPar++) {
813 cmbParam* pp = _params[iPar-1];
[3465]814 if ( AA.Column(iPar).maximum_absolute_value() > 0.0 &&
[3474]815 pp->type == cmbParam::clkSat ) {
816 AA(nObs+1, iPar) = 1.0;
[3455]817 }
818 }
[3474]819 int iCond = 1;
820 for (int iGps = 1; iGps <= MAXPRN_GPS; iGps++) {
821 QString prn = QString("G%1").arg(iGps, 2, 10, QChar('0'));
822 ++iCond;
823 PP(nObs+iCond) = Ph;
824 for (int iPar = 1; iPar <= _params.size(); iPar++) {
825 cmbParam* pp = _params[iPar-1];
826 if ( AA.Column(iPar).maximum_absolute_value() > 0.0 &&
827 pp->type == cmbParam::offACSat &&
828 pp->prn == prn) {
829 AA(nObs+iCond, iPar) = 1.0;
830 }
831 }
832 }
[3486]833// if (_useGlonass) {
834// for (int iGlo = 1; iGlo <= MAXPRN_GLONASS; iGlo++) {
835// QString prn = QString("R%1").arg(iGlo, 2, 10, QChar('0'));
836// ++iCond;
837// PP(nObs+iCond) = Ph;
838// for (int iPar = 1; iPar <= _params.size(); iPar++) {
839// cmbParam* pp = _params[iPar-1];
840// if ( AA.Column(iPar).maximum_absolute_value() > 0.0 &&
841// pp->type == cmbParam::offACSat &&
842// pp->prn == prn) {
843// AA(nObs+iCond, iPar) = 1.0;
844// }
845// }
846// }
847// }
[3455]848 }
849
850 return success;
851}
[3470]852
853// Process Epoch - Single-Epoch Method
854////////////////////////////////////////////////////////////////////////////
[3472]855t_irc bncComb::processEpoch_singleEpoch(QTextStream& out,
[3475]856 QMap<QString, t_corr*>& resCorr,
857 ColumnVector& dx) {
[3470]858
[3487]859 // Check Satellite Positions for Outliers
860 // --------------------------------------
[3497]861 if (checkOrbits(out) != success) {
[3487]862 return failure;
863 }
864
[3482]865 // Outlier Detection Loop
866 // ----------------------
867 while (true) {
868
869 // Remove Satellites that are not in Master
870 // ----------------------------------------
871 QMutableVectorIterator<cmbCorr*> it(corrs());
872 while (it.hasNext()) {
873 cmbCorr* corr = it.next();
874 QString prn = corr->prn;
875 bool foundMaster = false;
876 QVectorIterator<cmbCorr*> itHlp(corrs());
877 while (itHlp.hasNext()) {
878 cmbCorr* corrHlp = itHlp.next();
879 QString prnHlp = corrHlp->prn;
880 QString ACHlp = corrHlp->acName;
881 if (ACHlp == _masterOrbitAC && prn == prnHlp) {
882 foundMaster = true;
883 break;
884 }
[3476]885 }
[3482]886 if (!foundMaster) {
887 it.remove();
888 }
[3473]889 }
[3482]890
891 // Count Number of Observations per Satellite and per AC
892 // -----------------------------------------------------
893 QMap<QString, int> numObsPrn;
894 QMap<QString, int> numObsAC;
895 QVectorIterator<cmbCorr*> itCorr(corrs());
896 while (itCorr.hasNext()) {
897 cmbCorr* corr = itCorr.next();
898 QString prn = corr->prn;
899 QString AC = corr->acName;
900 if (numObsPrn.find(prn) == numObsPrn.end()) {
901 numObsPrn[prn] = 1;
902 }
903 else {
904 numObsPrn[prn] += 1;
905 }
906 if (numObsAC.find(AC) == numObsAC.end()) {
907 numObsAC[AC] = 1;
908 }
909 else {
910 numObsAC[AC] += 1;
911 }
[3476]912 }
[3482]913
914 // Clean-Up the Paramters
915 // ----------------------
916 for (int iPar = 1; iPar <= _params.size(); iPar++) {
917 delete _params[iPar-1];
[3474]918 }
[3482]919 _params.clear();
920
921 // Set new Parameters
922 // ------------------
923 int nextPar = 0;
924
925 QMapIterator<QString, int> itAC(numObsAC);
926 while (itAC.hasNext()) {
927 itAC.next();
928 const QString& AC = itAC.key();
929 int numObs = itAC.value();
930 if (AC != _masterOrbitAC && numObs > 0) {
[3485]931 _params.push_back(new cmbParam(cmbParam::offACgps, ++nextPar, AC, ""));
932 if (_useGlonass) {
933 _params.push_back(new cmbParam(cmbParam::offACglo, ++nextPar, AC, ""));
934 }
[3482]935 }
936 }
937
938 QMapIterator<QString, int> itPrn(numObsPrn);
939 while (itPrn.hasNext()) {
940 itPrn.next();
941 const QString& prn = itPrn.key();
942 int numObs = itPrn.value();
943 if (numObs > 0) {
944 _params.push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
945 }
946 }
947
948 int nPar = _params.size();
949 ColumnVector x0(nPar);
950 x0 = 0.0;
951
952 // Create First-Design Matrix
953 // --------------------------
954 Matrix AA;
955 ColumnVector ll;
956 DiagonalMatrix PP;
957 if (createAmat(AA, ll, PP, x0, resCorr) != success) {
958 return failure;
[3476]959 }
[3482]960
961 ColumnVector vv;
962 try {
963 Matrix ATP = AA.t() * PP;
964 SymmetricMatrix NN; NN << ATP * AA;
965 ColumnVector bb = ATP * ll;
966 _QQ = NN.i();
967 dx = _QQ * bb;
968 vv = ll - AA * dx;
[3476]969 }
[3482]970 catch (Exception& exc) {
971 out << exc.what() << endl;
972 return failure;
[3476]973 }
[3474]974
[3482]975 int maxResIndex;
976 double maxRes = vv.maximum_absolute_value1(maxResIndex);
977 out.setRealNumberNotation(QTextStream::FixedNotation);
978 out.setRealNumberPrecision(3);
979 out << _resTime.datestr().c_str() << " " << _resTime.timestr().c_str()
980 << " Maximum Residuum " << maxRes << ' '
981 << corrs()[maxResIndex-1]->acName << ' ' << corrs()[maxResIndex-1]->prn;
[3474]982
[3482]983 if (maxRes > _MAXRES) {
984 out << " Outlier" << endl;
985 corrs().remove(maxResIndex-1);
[3474]986 }
[3482]987 else {
988 out << " OK" << endl;
989 out.setRealNumberNotation(QTextStream::FixedNotation);
990 out.setRealNumberPrecision(3);
991 for (int ii = 0; ii < vv.Nrows(); ii++) {
992 const cmbCorr* corr = corrs()[ii];
993 out << _resTime.datestr().c_str() << ' '
994 << _resTime.timestr().c_str() << " "
995 << corr->acName << ' ' << corr->prn;
996 out.setFieldWidth(6);
997 out << " dClk = " << corr->dClk * t_CST::c << " res = " << vv[ii] << endl;
998 out.setFieldWidth(0);
999 }
1000 return success;
[3474]1001 }
1002
[3473]1003 }
[3474]1004
[3482]1005 return failure;
[3470]1006}
[3487]1007
1008// Check Satellite Positions for Outliers
1009////////////////////////////////////////////////////////////////////////////
[3497]1010t_irc bncComb::checkOrbits(QTextStream& out) {
[3487]1011
[3489]1012 const double MAX_DISPLACEMENT = 0.20;
[3488]1013
[3502]1014 // Switch to last ephemeris (if possible)
1015 // --------------------------------------
[3501]1016 QMutableVectorIterator<cmbCorr*> im(corrs());
1017 while (im.hasNext()) {
1018 cmbCorr* corr = im.next();
[3502]1019 QString prn = corr->prn;
[3503]1020 if (_eph.find(prn) == _eph.end()) {
[3502]1021 out << "checkOrbit: missing eph (not found) " << corr->prn << endl;
[3501]1022 im.remove();
1023 }
[3503]1024 else if (corr->eph == 0) {
1025 out << "checkOrbit: missing eph (zero) " << corr->prn << endl;
1026 im.remove();
1027 }
[3502]1028 else {
1029 if ( corr->eph == _eph[prn]->last || corr->eph == _eph[prn]->prev ) {
1030 switchToLastEph(_eph[prn]->last, corr);
1031 }
1032 else {
1033 out << "checkOrbit: missing eph (deleted) " << corr->prn << endl;
1034 im.remove();
1035 }
1036 }
[3501]1037 }
1038
[3488]1039 while (true) {
1040
1041 // Compute Mean Corrections for all Satellites
1042 // -------------------------------------------
[3489]1043 QMap<QString, int> numCorr;
[3488]1044 QMap<QString, ColumnVector> meanRao;
1045 QVectorIterator<cmbCorr*> it(corrs());
1046 while (it.hasNext()) {
1047 cmbCorr* corr = it.next();
1048 QString prn = corr->prn;
1049 if (meanRao.find(prn) == meanRao.end()) {
1050 meanRao[prn].ReSize(4);
1051 meanRao[prn].Rows(1,3) = corr->rao;
1052 meanRao[prn](4) = 1;
1053 }
1054 else {
1055 meanRao[prn].Rows(1,3) += corr->rao;
1056 meanRao[prn](4) += 1;
1057 }
[3489]1058 if (numCorr.find(prn) == numCorr.end()) {
1059 numCorr[prn] = 1;
1060 }
1061 else {
1062 numCorr[prn] += 1;
1063 }
[3487]1064 }
[3488]1065
1066 // Compute Differences wrt Mean, find Maximum
1067 // ------------------------------------------
1068 QMap<QString, cmbCorr*> maxDiff;
1069 it.toFront();
1070 while (it.hasNext()) {
1071 cmbCorr* corr = it.next();
1072 QString prn = corr->prn;
1073 if (meanRao[prn](4) != 0) {
1074 meanRao[prn] /= meanRao[prn](4);
1075 meanRao[prn](4) = 0;
1076 }
1077 corr->diffRao = corr->rao - meanRao[prn].Rows(1,3);
1078 if (maxDiff.find(prn) == maxDiff.end()) {
1079 maxDiff[prn] = corr;
1080 }
1081 else {
1082 double normMax = maxDiff[prn]->diffRao.norm_Frobenius();
1083 double norm = corr->diffRao.norm_Frobenius();
1084 if (norm > normMax) {
1085 maxDiff[prn] = corr;
1086 }
1087 }
[3487]1088 }
[3488]1089
1090 // Remove Outliers
1091 // ---------------
1092 bool removed = false;
1093 QMutableVectorIterator<cmbCorr*> im(corrs());
1094 while (im.hasNext()) {
1095 cmbCorr* corr = im.next();
1096 QString prn = corr->prn;
[3489]1097 if (numCorr[prn] < 2) {
1098 im.remove();
1099 }
1100 else if (corr == maxDiff[prn]) {
[3488]1101 double norm = corr->diffRao.norm_Frobenius();
1102 if (norm > MAX_DISPLACEMENT) {
[3497]1103 out << _resTime.datestr().c_str() << " "
1104 << _resTime.timestr().c_str() << " "
1105 << "Orbit Outlier: "
1106 << corr->acName.toAscii().data() << " "
1107 << prn.toAscii().data() << " "
1108 << corr->iod << " "
1109 << norm << endl;
[3488]1110 im.remove();
1111 removed = true;
1112 }
1113 }
[3487]1114 }
[3488]1115
1116 if (!removed) {
1117 break;
1118 }
[3487]1119 }
1120
1121 return success;
1122}
Note: See TracBrowser for help on using the repository browser.