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

Last change on this file since 5584 was 5584, checked in by mervart, 10 years ago
File size: 32.2 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"
[5070]22#include "bnccore.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 double sig0_offAC = 1000.0;
33const double sig0_offACSat = 100.0;
[3468]34const double sigP_offACSat = 0.01;
[3455]35const double sig0_clkSat = 100.0;
36
37const double sigObs = 0.05;
38
[3483]39const int MAXPRN_GLONASS = 24;
[3134]40
[3455]41using namespace std;
42
[2898]43// Constructor
44////////////////////////////////////////////////////////////////////////////
[3429]45cmbParam::cmbParam(parType type_, int index_,
46 const QString& ac_, const QString& prn_) {
[3032]47
[3433]48 type = type_;
49 index = index_;
50 AC = ac_;
51 prn = prn_;
52 xx = 0.0;
[3455]53 eph = 0;
[3429]54
[3485]55 if (type == offACgps) {
[3455]56 epoSpec = true;
57 sig0 = sig0_offAC;
58 sigP = sig0;
[3429]59 }
[3485]60 else if (type == offACglo) {
61 epoSpec = true;
62 sig0 = sig0_offAC;
63 sigP = sig0;
64 }
[3429]65 else if (type == offACSat) {
[3455]66 epoSpec = false;
67 sig0 = sig0_offACSat;
68 sigP = sigP_offACSat;
[3429]69 }
70 else if (type == clkSat) {
[3455]71 epoSpec = true;
72 sig0 = sig0_clkSat;
73 sigP = sig0;
[3429]74 }
[2933]75}
76
77// Destructor
78////////////////////////////////////////////////////////////////////////////
79cmbParam::~cmbParam() {
80}
81
82// Partial
83////////////////////////////////////////////////////////////////////////////
[3429]84double cmbParam::partial(const QString& AC_, const QString& prn_) {
[2933]85
[3485]86 if (type == offACgps) {
87 if (AC == AC_ && prn_[0] == 'G') {
[2934]88 return 1.0;
89 }
90 }
[3485]91 else if (type == offACglo) {
92 if (AC == AC_ && prn_[0] == 'R') {
93 return 1.0;
94 }
95 }
[3429]96 else if (type == offACSat) {
97 if (AC == AC_ && prn == prn_) {
[2933]98 return 1.0;
99 }
100 }
[3429]101 else if (type == clkSat) {
102 if (prn == prn_) {
[2933]103 return 1.0;
104 }
105 }
106
107 return 0.0;
108}
109
[2990]110//
111////////////////////////////////////////////////////////////////////////////
112QString cmbParam::toString() const {
[2933]113
[2990]114 QString outStr;
115
[3485]116 if (type == offACgps) {
117 outStr = "AC offset GPS " + AC;
[2990]118 }
[3485]119 else if (type == offACglo) {
120 outStr = "AC offset GLO " + AC;
121 }
[3429]122 else if (type == offACSat) {
[2992]123 outStr = "Sat Offset " + AC + " " + prn;
[2990]124 }
[3429]125 else if (type == clkSat) {
[2992]126 outStr = "Clk Corr " + prn;
[2990]127 }
[2933]128
[2990]129 return outStr;
130}
131
[2933]132// Constructor
133////////////////////////////////////////////////////////////////////////////
[2898]134bncComb::bncComb() {
[2910]135
136 bncSettings settings;
137
138 QStringList combineStreams = settings.value("combineStreams").toStringList();
[2918]139
[4183]140 _cmbSampl = settings.value("cmbSampl").toInt();
141 if (_cmbSampl <= 0) {
142 _cmbSampl = 10;
143 }
144
[3455]145 _masterMissingEpochs = 0;
146
[3143]147 if (combineStreams.size() >= 1 && !combineStreams[0].isEmpty()) {
[2910]148 QListIterator<QString> it(combineStreams);
149 while (it.hasNext()) {
150 QStringList hlp = it.next().split(" ");
[2918]151 cmbAC* newAC = new cmbAC();
152 newAC->mountPoint = hlp[0];
153 newAC->name = hlp[1];
154 newAC->weight = hlp[2].toDouble();
[3453]155 if (_masterOrbitAC.isEmpty()) {
156 _masterOrbitAC = newAC->name;
157 }
[3429]158 _ACs.append(newAC);
[2910]159 }
160 }
[2927]161
[3211]162 _rtnetDecoder = 0;
[3201]163
[2990]164 connect(this, SIGNAL(newMessage(QByteArray,bool)),
[5068]165 BNC_CORE, SLOT(slotMessage(const QByteArray,bool)));
[2933]166
[5583]167 connect(BNC_CORE, SIGNAL(providerIDChanged(QString)),
168 this, SLOT(slotProviderIDChanged(QString)));
169
[3470]170 // Combination Method
171 // ------------------
[3481]172 if (settings.value("cmbMethod").toString() == "Single-Epoch") {
173 _method = singleEpoch;
[3470]174 }
175 else {
[3481]176 _method = filter;
[3470]177 }
[3032]178
[3484]179 // Use Glonass
180 // -----------
181 if ( Qt::CheckState(settings.value("pppGLONASS").toInt()) == Qt::Checked) {
182 _useGlonass = true;
183 }
184 else {
185 _useGlonass = false;
186 }
187
[3032]188 // Initialize Parameters (model: Clk_Corr = AC_Offset + Sat_Offset + Clk)
189 // ----------------------------------------------------------------------
[3470]190 if (_method == filter) {
191 int nextPar = 0;
192 QListIterator<cmbAC*> it(_ACs);
193 while (it.hasNext()) {
194 cmbAC* AC = it.next();
[3485]195 _params.push_back(new cmbParam(cmbParam::offACgps, ++nextPar, AC->name, ""));
[3470]196 for (int iGps = 1; iGps <= MAXPRN_GPS; iGps++) {
197 QString prn = QString("G%1").arg(iGps, 2, 10, QChar('0'));
198 _params.push_back(new cmbParam(cmbParam::offACSat, ++nextPar,
199 AC->name, prn));
200 }
[3483]201 if (_useGlonass) {
[3485]202 _params.push_back(new cmbParam(cmbParam::offACglo, ++nextPar, AC->name, ""));
[3483]203 for (int iGlo = 1; iGlo <= MAXPRN_GLONASS; iGlo++) {
204 QString prn = QString("R%1").arg(iGlo, 2, 10, QChar('0'));
205 _params.push_back(new cmbParam(cmbParam::offACSat, ++nextPar,
206 AC->name, prn));
207 }
208 }
[3470]209 }
[3134]210 for (int iGps = 1; iGps <= MAXPRN_GPS; iGps++) {
211 QString prn = QString("G%1").arg(iGps, 2, 10, QChar('0'));
[3470]212 _params.push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
[2991]213 }
[3483]214 if (_useGlonass) {
215 for (int iGlo = 1; iGlo <= MAXPRN_GLONASS; iGlo++) {
216 QString prn = QString("R%1").arg(iGlo, 2, 10, QChar('0'));
217 _params.push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
218 }
219 }
[3470]220
221 // Initialize Variance-Covariance Matrix
222 // -------------------------------------
223 _QQ.ReSize(_params.size());
224 _QQ = 0.0;
225 for (int iPar = 1; iPar <= _params.size(); iPar++) {
226 cmbParam* pp = _params[iPar-1];
227 _QQ(iPar,iPar) = pp->sig0 * pp->sig0;
228 }
[2991]229 }
[2933]230
[3052]231 // ANTEX File
232 // ----------
233 _antex = 0;
234 QString antexFileName = settings.value("pppAntex").toString();
235 if (!antexFileName.isEmpty()) {
236 _antex = new bncAntex();
237 if (_antex->readFile(antexFileName) != success) {
238 emit newMessage("wrong ANTEX file", true);
239 delete _antex;
240 _antex = 0;
241 }
242 }
[3138]243
[3329]244 // Maximal Residuum
245 // ----------------
246 _MAXRES = settings.value("cmbMaxres").toDouble();
247 if (_MAXRES <= 0.0) {
248 _MAXRES = 999.0;
249 }
[2898]250}
251
252// Destructor
253////////////////////////////////////////////////////////////////////////////
254bncComb::~bncComb() {
[3429]255 QListIterator<cmbAC*> icAC(_ACs);
256 while (icAC.hasNext()) {
257 delete icAC.next();
[2918]258 }
[3201]259 delete _rtnetDecoder;
[3052]260 delete _antex;
[3296]261 for (int iPar = 1; iPar <= _params.size(); iPar++) {
262 delete _params[iPar-1];
263 }
[3556]264 QListIterator<bncTime> itTime(_buffer.keys());
[3551]265 while (itTime.hasNext()) {
[3556]266 bncTime epoTime = itTime.next();
267 _buffer.remove(epoTime);
[3429]268 }
[3588]269 QMapIterator<QString, cmbCorr*> itOrbCorr(_orbitCorrs);
270 while (itOrbCorr.hasNext()) {
271 itOrbCorr.next();
272 delete itOrbCorr.value();
273 }
[2898]274}
275
[2928]276// Read and store one correction line
[2898]277////////////////////////////////////////////////////////////////////////////
278void bncComb::processCorrLine(const QString& staID, const QString& line) {
[2906]279 QMutexLocker locker(&_mutex);
[2913]280
[3431]281 // Find the AC Name
282 // ----------------
283 QString acName;
284 QListIterator<cmbAC*> icAC(_ACs);
285 while (icAC.hasNext()) {
286 cmbAC* AC = icAC.next();
287 if (AC->mountPoint == staID) {
288 acName = AC->name;
289 break;
290 }
291 }
292 if (acName.isEmpty()) {
293 return;
294 }
295
[2918]296 // Read the Correction
297 // -------------------
[3429]298 cmbCorr* newCorr = new cmbCorr();
[3431]299 newCorr->acName = acName;
[2913]300 if (!newCorr->readLine(line) == success) {
301 delete newCorr;
302 return;
303 }
304
[3483]305 // Check Glonass
306 // -------------
307 if (!_useGlonass) {
308 if (newCorr->prn[0] == 'R') {
309 delete newCorr;
310 return;
311 }
312 }
313
[3588]314 // Save Orbit-Only Corrections
315 // ---------------------------
[3751]316 if (newCorr->tRao.valid() && !newCorr->tClk.valid()) {
[3588]317 QString corrID = newCorr->ID();
318 if (_orbitCorrs.find(corrID) != _orbitCorrs.end()) {
319 delete _orbitCorrs[corrID];
320 }
[3590]321 _orbitCorrs[corrID] = newCorr;
322 return;
[3588]323 }
324
[3590]325 // Merge with saved orbit correction
326 // ---------------------------------
[3751]327 else if (newCorr->tClk.valid() && !newCorr->tRao.valid()) {
[3590]328 QString corrID = newCorr->ID();
329 if (_orbitCorrs.find(corrID) != _orbitCorrs.end()) {
330 mergeOrbitCorr(_orbitCorrs[corrID], newCorr);
331 }
332 }
333
[3274]334 // Check Modulo Time
335 // -----------------
[4183]336 if (int(newCorr->tClk.gpssec()) % _cmbSampl != 0.0) {
[3274]337 delete newCorr;
338 return;
339 }
340
[3299]341 // Delete old corrections
342 // ----------------------
[3751]343 if (_resTime.valid() && newCorr->tClk <= _resTime) {
[5127]344 emit newMessage("bncComb: old correction: " + staID.toAscii() + " " + line.toAscii(), true);
[2924]345 delete newCorr;
346 return;
347 }
348
[3029]349 // Check the Ephemeris
350 //--------------------
[2986]351 if (_eph.find(newCorr->prn) == _eph.end()) {
[5129]352 emit newMessage("bncComb: eph not found " + newCorr->prn.toAscii(), true);
[2986]353 delete newCorr;
354 return;
355 }
356 else {
357 t_eph* lastEph = _eph[newCorr->prn]->last;
358 t_eph* prevEph = _eph[newCorr->prn]->prev;
[3029]359 if (lastEph && lastEph->IOD() == newCorr->iod) {
360 newCorr->eph = lastEph;
[2986]361 }
[3501]362 else if (lastEph && prevEph && prevEph->IOD() == newCorr->iod) {
[3029]363 newCorr->eph = prevEph;
[3429]364 switchToLastEph(lastEph, newCorr);
[3029]365 }
366 else {
[5129]367 emit newMessage("bncComb: eph not found " + newCorr->prn.toAscii() +
368 QString(" %1").arg(newCorr->iod).toAscii(), true);
[2986]369 delete newCorr;
370 return;
371 }
372 }
373
[3435]374 // Process previous Epoch(s)
375 // -------------------------
[5134]376 const double waitTime = 1.0 * _cmbSampl;
[3556]377 QListIterator<bncTime> itTime(_buffer.keys());
[3435]378 while (itTime.hasNext()) {
[3556]379 bncTime epoTime = itTime.next();
[5129]380 if (epoTime < newCorr->tClk - waitTime) {
[3556]381 _resTime = epoTime;
[3435]382 processEpoch();
383 }
[2927]384 }
385
[3429]386 // Merge or add the correction
387 // ---------------------------
[3751]388 QVector<cmbCorr*>& corrs = _buffer[newCorr->tClk].corrs;
[3429]389 cmbCorr* existingCorr = 0;
[3435]390 QVectorIterator<cmbCorr*> itCorr(corrs);
[3429]391 while (itCorr.hasNext()) {
392 cmbCorr* hlp = itCorr.next();
[3554]393 if (hlp->prn == newCorr->prn && hlp->acName == newCorr->acName) {
[3429]394 existingCorr = hlp;
[2918]395 break;
396 }
397 }
[3429]398 if (existingCorr) {
[3588]399 delete newCorr; newCorr = 0;
[3429]400 existingCorr->readLine(line); // merge (multiple messages)
[3588]401
[2918]402 }
[2919]403 else {
[3435]404 corrs.append(newCorr);
[2919]405 }
[2898]406}
407
[2986]408// Change the correction so that it refers to last received ephemeris
409////////////////////////////////////////////////////////////////////////////
[3029]410void bncComb::switchToLastEph(const t_eph* lastEph, t_corr* corr) {
[3028]411
[3429]412 if (corr->eph == lastEph) {
413 return;
414 }
415
[2987]416 ColumnVector oldXC(4);
417 ColumnVector oldVV(3);
[3751]418 corr->eph->position(corr->tRao.gpsw(), corr->tRao.gpssec(),
[3029]419 oldXC.data(), oldVV.data());
[2988]420
[2987]421 ColumnVector newXC(4);
422 ColumnVector newVV(3);
[3751]423 lastEph->position(corr->tRao.gpsw(), corr->tRao.gpssec(),
[2987]424 newXC.data(), newVV.data());
[2988]425
426 ColumnVector dX = newXC.Rows(1,3) - oldXC.Rows(1,3);
[2989]427 ColumnVector dV = newVV - oldVV;
428 double dC = newXC(4) - oldXC(4);
429
[2988]430 ColumnVector dRAO(3);
431 XYZ_to_RSW(newXC.Rows(1,3), newVV, dX, dRAO);
[2989]432
433 ColumnVector dDotRAO(3);
434 XYZ_to_RSW(newXC.Rows(1,3), newVV, dV, dDotRAO);
435
[3455]436 QString msg = "switch corr " + corr->prn
[3029]437 + QString(" %1 -> %2 %3").arg(corr->iod,3)
[3013]438 .arg(lastEph->IOD(),3).arg(dC*t_CST::c, 8, 'f', 4);
439
[3029]440 emit newMessage(msg.toAscii(), false);
[3028]441
[3029]442 corr->iod = lastEph->IOD();
443 corr->eph = lastEph;
[3031]444 corr->rao += dRAO;
445 corr->dotRao += dDotRAO;
[3029]446 corr->dClk -= dC;
[2986]447}
448
[3429]449// Process Epoch
[2928]450////////////////////////////////////////////////////////////////////////////
[3429]451void bncComb::processEpoch() {
[2918]452
[2990]453 _log.clear();
454
455 QTextStream out(&_log, QIODevice::WriteOnly);
456
[3472]457 out << endl << "Combination:" << endl
458 << "------------------------------" << endl;
[2990]459
[3433]460 // Observation Statistics
461 // ----------------------
[3455]462 bool masterPresent = false;
[3433]463 QListIterator<cmbAC*> icAC(_ACs);
464 while (icAC.hasNext()) {
465 cmbAC* AC = icAC.next();
466 AC->numObs = 0;
[3434]467 QVectorIterator<cmbCorr*> itCorr(corrs());
[3433]468 while (itCorr.hasNext()) {
469 cmbCorr* corr = itCorr.next();
470 if (corr->acName == AC->name) {
471 AC->numObs += 1;
[3453]472 if (AC->name == _masterOrbitAC) {
473 masterPresent = true;
474 }
[3433]475 }
476 }
477 out << AC->name.toAscii().data() << ": " << AC->numObs << endl;
478 }
479
[3453]480 // If Master not present, switch to another one
481 // --------------------------------------------
[4889]482 const unsigned switchMasterAfterGap = 1;
[3456]483 if (masterPresent) {
484 _masterMissingEpochs = 0;
485 }
486 else {
[3455]487 ++_masterMissingEpochs;
[4889]488 if (_masterMissingEpochs < switchMasterAfterGap) {
[3457]489 out << "Missing Master, Epoch skipped" << endl;
[3556]490 _buffer.remove(_resTime);
[3455]491 emit newMessage(_log, false);
492 return;
[3453]493 }
[3455]494 else {
495 _masterMissingEpochs = 0;
496 QListIterator<cmbAC*> icAC(_ACs);
497 while (icAC.hasNext()) {
498 cmbAC* AC = icAC.next();
499 if (AC->numObs > 0) {
500 out << "Switching Master AC "
501 << _masterOrbitAC.toAscii().data() << " --> "
502 << AC->name.toAscii().data() << " "
503 << _resTime.datestr().c_str() << " "
504 << _resTime.timestr().c_str() << endl;
505 _masterOrbitAC = AC->name;
506 break;
507 }
[3452]508 }
509 }
510 }
511
[3472]512 QMap<QString, t_corr*> resCorr;
513
514 // Perform the actual Combination using selected Method
515 // ----------------------------------------------------
516 t_irc irc;
[3475]517 ColumnVector dx;
[3472]518 if (_method == filter) {
[3475]519 irc = processEpoch_filter(out, resCorr, dx);
[3472]520 }
521 else {
[3475]522 irc = processEpoch_singleEpoch(out, resCorr, dx);
[3472]523 }
524
[3475]525 // Update Parameter Values, Print Results
526 // --------------------------------------
[3472]527 if (irc == success) {
[3475]528 for (int iPar = 1; iPar <= _params.size(); iPar++) {
529 cmbParam* pp = _params[iPar-1];
530 pp->xx += dx(iPar);
531 if (pp->type == cmbParam::clkSat) {
532 if (resCorr.find(pp->prn) != resCorr.end()) {
533 resCorr[pp->prn]->dClk = pp->xx / t_CST::c;
534 }
535 }
536 out << _resTime.datestr().c_str() << " "
537 << _resTime.timestr().c_str() << " ";
538 out.setRealNumberNotation(QTextStream::FixedNotation);
539 out.setFieldWidth(8);
540 out.setRealNumberPrecision(4);
541 out << pp->toString() << " "
542 << pp->xx << " +- " << sqrt(_QQ(pp->index,pp->index)) << endl;
543 out.setFieldWidth(0);
544 }
[3472]545 printResults(out, resCorr);
546 dumpResults(resCorr);
547 }
548
549 // Delete Data, emit Message
550 // -------------------------
[3556]551 _buffer.remove(_resTime);
[3472]552 emit newMessage(_log, false);
553}
554
555// Process Epoch - Filter Method
556////////////////////////////////////////////////////////////////////////////
557t_irc bncComb::processEpoch_filter(QTextStream& out,
[3475]558 QMap<QString, t_corr*>& resCorr,
559 ColumnVector& dx) {
[3472]560
[3429]561 // Prediction Step
562 // ---------------
[3472]563 int nPar = _params.size();
[2933]564 ColumnVector x0(nPar);
565 for (int iPar = 1; iPar <= _params.size(); iPar++) {
[3455]566 cmbParam* pp = _params[iPar-1];
567 if (pp->epoSpec) {
[3451]568 pp->xx = 0.0;
[3455]569 _QQ.Row(iPar) = 0.0;
570 _QQ.Column(iPar) = 0.0;
571 _QQ(iPar,iPar) = pp->sig0 * pp->sig0;
[3451]572 }
[3455]573 else {
574 _QQ(iPar,iPar) += pp->sigP * pp->sigP;
575 }
[2933]576 x0(iPar) = pp->xx;
577 }
578
[3487]579 // Check Satellite Positions for Outliers
580 // --------------------------------------
[3497]581 if (checkOrbits(out) != success) {
[3487]582 return failure;
583 }
[3441]584
[3455]585 // Update and outlier detection loop
586 // ---------------------------------
[3487]587 SymmetricMatrix QQ_sav = _QQ;
[3455]588 while (true) {
[3135]589
[3455]590 Matrix AA;
591 ColumnVector ll;
592 DiagonalMatrix PP;
[3429]593
[3455]594 if (createAmat(AA, ll, PP, x0, resCorr) != success) {
[3472]595 return failure;
[3441]596 }
[2933]597
[3429]598 bncModel::kalman(AA, ll, PP, _QQ, dx);
599 ColumnVector vv = ll - AA * dx;
[3080]600
[3429]601 int maxResIndex;
602 double maxRes = vv.maximum_absolute_value1(maxResIndex);
603 out.setRealNumberNotation(QTextStream::FixedNotation);
604 out.setRealNumberPrecision(3);
605 out << _resTime.datestr().c_str() << " " << _resTime.timestr().c_str()
[3432]606 << " Maximum Residuum " << maxRes << ' '
[3434]607 << corrs()[maxResIndex-1]->acName << ' ' << corrs()[maxResIndex-1]->prn;
[3080]608
[3432]609 if (maxRes > _MAXRES) {
610 for (int iPar = 1; iPar <= _params.size(); iPar++) {
611 cmbParam* pp = _params[iPar-1];
612 if (pp->type == cmbParam::offACSat &&
[3434]613 pp->AC == corrs()[maxResIndex-1]->acName &&
614 pp->prn == corrs()[maxResIndex-1]->prn) {
[3432]615 QQ_sav.Row(iPar) = 0.0;
616 QQ_sav.Column(iPar) = 0.0;
[3455]617 QQ_sav(iPar,iPar) = pp->sig0 * pp->sig0;
[3432]618 }
619 }
620
621 out << " Outlier" << endl;
622 _QQ = QQ_sav;
[3455]623 corrs().remove(maxResIndex-1);
[3432]624 }
625 else {
626 out << " OK" << endl;
627 break;
628 }
629 }
630
[3472]631 return success;
[2918]632}
[3011]633
[3201]634// Print results
[3011]635////////////////////////////////////////////////////////////////////////////
[3429]636void bncComb::printResults(QTextStream& out,
[3011]637 const QMap<QString, t_corr*>& resCorr) {
638
639 QMapIterator<QString, t_corr*> it(resCorr);
640 while (it.hasNext()) {
641 it.next();
642 t_corr* corr = it.value();
[3029]643 const t_eph* eph = corr->eph;
[3015]644 if (eph) {
645 double xx, yy, zz, cc;
[3429]646 eph->position(_resTime.gpsw(), _resTime.gpssec(), xx, yy, zz, cc);
[3015]647
[3429]648 out << _resTime.datestr().c_str() << " "
649 << _resTime.timestr().c_str() << " ";
[3015]650 out.setFieldWidth(3);
651 out << "Full Clock " << corr->prn << " " << corr->iod << " ";
652 out.setFieldWidth(14);
653 out << (cc + corr->dClk) * t_CST::c << endl;
[3370]654 out.setFieldWidth(0);
[3015]655 }
656 else {
657 out << "bncComb::printResuls bug" << endl;
658 }
[3011]659 }
660}
[3202]661
662// Send results to RTNet Decoder and directly to PPP Client
663////////////////////////////////////////////////////////////////////////////
[3429]664void bncComb::dumpResults(const QMap<QString, t_corr*>& resCorr) {
[3202]665
[5337]666 QString outLines;
667 QStringList corrLines;
[3214]668
669 unsigned year, month, day, hour, minute;
670 double sec;
[3429]671 _resTime.civil_date(year, month, day);
672 _resTime.civil_time(hour, minute, sec);
[3214]673
[5337]674 outLines.sprintf("* %4d %2d %2d %d %d %12.8f\n",
675 year, month, day, hour, minute, sec);
[3214]676
[3202]677 QMapIterator<QString, t_corr*> it(resCorr);
678 while (it.hasNext()) {
679 it.next();
680 t_corr* corr = it.value();
681
[4978]682 ColumnVector xc(4);
683 ColumnVector vv(3);
684 corr->eph->position(_resTime.gpsw(), _resTime.gpssec(),
685 xc.data(), vv.data());
686 bncPPPclient::applyCorr(_resTime, corr, xc, vv);
687
688 // Correction Phase Center --> CoM
689 // -------------------------------
690 ColumnVector dx(3); dx = 0.0;
691 if (_antex) {
692 double Mjd = _resTime.mjd() + _resTime.daysec()/86400.0;
[4992]693 if (_antex->satCoMcorrection(corr->prn, Mjd, xc.Rows(1,3), dx) != success) {
694 dx = 0;
[5119]695 cout << "antenna not found " << corr->prn.toAscii().data() << endl;
[3214]696 }
697 }
[4978]698
[5337]699 outLines += corr->prn;
700 QString hlp;
701 hlp.sprintf(" APC 3 %15.4f %15.4f %15.4f"
702 " Clk 1 %15.4f"
703 " Vel 3 %15.4f %15.4f %15.4f"
704 " CoM 3 %15.4f %15.4f %15.4f\n",
705 xc(1), xc(2), xc(3),
706 xc(4)*t_CST::c,
707 vv(1), vv(2), vv(3),
708 xc(1)-dx(1), xc(2)-dx(2), xc(3)-dx(3));
709 outLines += hlp;
710
[4978]711 QString line;
[5564]712 int messageType = COTYPE_GPSCOMBINED;
713 int updateInt = 0;
[4978]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"
[5576]718 " %10.5f INTERNAL",
[4978]719 messageType, updateInt, _resTime.gpsw(), _resTime.gpssec(),
720 corr->prn.toAscii().data(),
721 corr->iod,
722 corr->dClk * t_CST::c,
723 corr->rao[0],
724 corr->rao[1],
725 corr->rao[2],
726 corr->dotDClk * t_CST::c,
727 corr->dotRao[0],
728 corr->dotRao[1],
729 corr->dotRao[2],
[5576]730 corr->dotDotDClk * t_CST::c);
[4978]731 corrLines << line;
732
[3214]733 delete corr;
734 }
735
[5337]736 outLines += "EOE\n"; // End Of Epoch flag
737
[3215]738 if (!_rtnetDecoder) {
739 _rtnetDecoder = new bncRtnetDecoder();
740 }
[3221]741
[3215]742 vector<string> errmsg;
[5337]743 _rtnetDecoder->Decode(outLines.toAscii().data(), outLines.length(), errmsg);
[3215]744
[3216]745 // Optionally send new Corrections to PPP
746 // --------------------------------------
[5068]747 if (BNC_CORE->_bncPPPclient) {
748 BNC_CORE->_bncPPPclient->slotNewCorrections(corrLines);
[3216]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) {
[3556]877 delete corr;
[3482]878 it.remove();
879 }
[3473]880 }
[3482]881
882 // Count Number of Observations per Satellite and per AC
883 // -----------------------------------------------------
884 QMap<QString, int> numObsPrn;
885 QMap<QString, int> numObsAC;
886 QVectorIterator<cmbCorr*> itCorr(corrs());
887 while (itCorr.hasNext()) {
888 cmbCorr* corr = itCorr.next();
889 QString prn = corr->prn;
890 QString AC = corr->acName;
891 if (numObsPrn.find(prn) == numObsPrn.end()) {
892 numObsPrn[prn] = 1;
893 }
894 else {
895 numObsPrn[prn] += 1;
896 }
897 if (numObsAC.find(AC) == numObsAC.end()) {
898 numObsAC[AC] = 1;
899 }
900 else {
901 numObsAC[AC] += 1;
902 }
[3476]903 }
[3482]904
905 // Clean-Up the Paramters
906 // ----------------------
907 for (int iPar = 1; iPar <= _params.size(); iPar++) {
908 delete _params[iPar-1];
[3474]909 }
[3482]910 _params.clear();
911
912 // Set new Parameters
913 // ------------------
914 int nextPar = 0;
915
916 QMapIterator<QString, int> itAC(numObsAC);
917 while (itAC.hasNext()) {
918 itAC.next();
919 const QString& AC = itAC.key();
920 int numObs = itAC.value();
921 if (AC != _masterOrbitAC && numObs > 0) {
[3485]922 _params.push_back(new cmbParam(cmbParam::offACgps, ++nextPar, AC, ""));
923 if (_useGlonass) {
924 _params.push_back(new cmbParam(cmbParam::offACglo, ++nextPar, AC, ""));
925 }
[3482]926 }
927 }
928
929 QMapIterator<QString, int> itPrn(numObsPrn);
930 while (itPrn.hasNext()) {
931 itPrn.next();
932 const QString& prn = itPrn.key();
933 int numObs = itPrn.value();
934 if (numObs > 0) {
935 _params.push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
936 }
937 }
938
939 int nPar = _params.size();
940 ColumnVector x0(nPar);
941 x0 = 0.0;
942
943 // Create First-Design Matrix
944 // --------------------------
945 Matrix AA;
946 ColumnVector ll;
947 DiagonalMatrix PP;
948 if (createAmat(AA, ll, PP, x0, resCorr) != success) {
949 return failure;
[3476]950 }
[3482]951
952 ColumnVector vv;
953 try {
954 Matrix ATP = AA.t() * PP;
955 SymmetricMatrix NN; NN << ATP * AA;
956 ColumnVector bb = ATP * ll;
957 _QQ = NN.i();
958 dx = _QQ * bb;
959 vv = ll - AA * dx;
[3476]960 }
[3482]961 catch (Exception& exc) {
962 out << exc.what() << endl;
963 return failure;
[3476]964 }
[3474]965
[3482]966 int maxResIndex;
967 double maxRes = vv.maximum_absolute_value1(maxResIndex);
968 out.setRealNumberNotation(QTextStream::FixedNotation);
969 out.setRealNumberPrecision(3);
970 out << _resTime.datestr().c_str() << " " << _resTime.timestr().c_str()
971 << " Maximum Residuum " << maxRes << ' '
972 << corrs()[maxResIndex-1]->acName << ' ' << corrs()[maxResIndex-1]->prn;
[3474]973
[3482]974 if (maxRes > _MAXRES) {
975 out << " Outlier" << endl;
[3556]976 delete corrs()[maxResIndex-1];
[3482]977 corrs().remove(maxResIndex-1);
[3474]978 }
[3482]979 else {
980 out << " OK" << endl;
981 out.setRealNumberNotation(QTextStream::FixedNotation);
982 out.setRealNumberPrecision(3);
983 for (int ii = 0; ii < vv.Nrows(); ii++) {
984 const cmbCorr* corr = corrs()[ii];
985 out << _resTime.datestr().c_str() << ' '
986 << _resTime.timestr().c_str() << " "
987 << corr->acName << ' ' << corr->prn;
988 out.setFieldWidth(6);
989 out << " dClk = " << corr->dClk * t_CST::c << " res = " << vv[ii] << endl;
990 out.setFieldWidth(0);
991 }
992 return success;
[3474]993 }
994
[3473]995 }
[3474]996
[3482]997 return failure;
[3470]998}
[3487]999
1000// Check Satellite Positions for Outliers
1001////////////////////////////////////////////////////////////////////////////
[3497]1002t_irc bncComb::checkOrbits(QTextStream& out) {
[3487]1003
[3489]1004 const double MAX_DISPLACEMENT = 0.20;
[3488]1005
[3502]1006 // Switch to last ephemeris (if possible)
1007 // --------------------------------------
[3501]1008 QMutableVectorIterator<cmbCorr*> im(corrs());
1009 while (im.hasNext()) {
1010 cmbCorr* corr = im.next();
[3502]1011 QString prn = corr->prn;
[3503]1012 if (_eph.find(prn) == _eph.end()) {
[3502]1013 out << "checkOrbit: missing eph (not found) " << corr->prn << endl;
[3556]1014 delete corr;
[3501]1015 im.remove();
1016 }
[3503]1017 else if (corr->eph == 0) {
1018 out << "checkOrbit: missing eph (zero) " << corr->prn << endl;
[3556]1019 delete corr;
[3503]1020 im.remove();
1021 }
[3502]1022 else {
1023 if ( corr->eph == _eph[prn]->last || corr->eph == _eph[prn]->prev ) {
1024 switchToLastEph(_eph[prn]->last, corr);
1025 }
1026 else {
1027 out << "checkOrbit: missing eph (deleted) " << corr->prn << endl;
[3556]1028 delete corr;
[3502]1029 im.remove();
1030 }
1031 }
[3501]1032 }
1033
[3488]1034 while (true) {
1035
1036 // Compute Mean Corrections for all Satellites
1037 // -------------------------------------------
[3489]1038 QMap<QString, int> numCorr;
[3488]1039 QMap<QString, ColumnVector> meanRao;
1040 QVectorIterator<cmbCorr*> it(corrs());
1041 while (it.hasNext()) {
1042 cmbCorr* corr = it.next();
1043 QString prn = corr->prn;
1044 if (meanRao.find(prn) == meanRao.end()) {
1045 meanRao[prn].ReSize(4);
1046 meanRao[prn].Rows(1,3) = corr->rao;
1047 meanRao[prn](4) = 1;
1048 }
1049 else {
1050 meanRao[prn].Rows(1,3) += corr->rao;
1051 meanRao[prn](4) += 1;
1052 }
[3489]1053 if (numCorr.find(prn) == numCorr.end()) {
1054 numCorr[prn] = 1;
1055 }
1056 else {
1057 numCorr[prn] += 1;
1058 }
[3487]1059 }
[3488]1060
1061 // Compute Differences wrt Mean, find Maximum
1062 // ------------------------------------------
1063 QMap<QString, cmbCorr*> maxDiff;
1064 it.toFront();
1065 while (it.hasNext()) {
1066 cmbCorr* corr = it.next();
1067 QString prn = corr->prn;
1068 if (meanRao[prn](4) != 0) {
1069 meanRao[prn] /= meanRao[prn](4);
1070 meanRao[prn](4) = 0;
1071 }
1072 corr->diffRao = corr->rao - meanRao[prn].Rows(1,3);
1073 if (maxDiff.find(prn) == maxDiff.end()) {
1074 maxDiff[prn] = corr;
1075 }
1076 else {
1077 double normMax = maxDiff[prn]->diffRao.norm_Frobenius();
1078 double norm = corr->diffRao.norm_Frobenius();
1079 if (norm > normMax) {
1080 maxDiff[prn] = corr;
1081 }
1082 }
[3487]1083 }
[3488]1084
[3655]1085 if (_ACs.size() == 1) {
1086 break;
1087 }
1088
[3488]1089 // Remove Outliers
1090 // ---------------
1091 bool removed = false;
1092 QMutableVectorIterator<cmbCorr*> im(corrs());
1093 while (im.hasNext()) {
1094 cmbCorr* corr = im.next();
1095 QString prn = corr->prn;
[3489]1096 if (numCorr[prn] < 2) {
[3556]1097 delete corr;
[3489]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;
[3556]1110 delete corr;
[3488]1111 im.remove();
1112 removed = true;
1113 }
1114 }
[3487]1115 }
[3488]1116
1117 if (!removed) {
1118 break;
1119 }
[3487]1120 }
1121
1122 return success;
1123}
[3588]1124
1125//
1126////////////////////////////////////////////////////////////////////////////
1127t_irc bncComb::mergeOrbitCorr(const cmbCorr* orbitCorr, cmbCorr* clkCorr) {
1128
[3751]1129 clkCorr->iod = orbitCorr->iod; // is it always correct?
1130 clkCorr->eph = orbitCorr->eph;
1131 clkCorr->tRao = orbitCorr->tRao;
1132 clkCorr->rao = orbitCorr->rao;
1133 clkCorr->dotRao = orbitCorr->dotRao;
[3589]1134
[3588]1135 return success;
1136}
[5583]1137
1138//
1139////////////////////////////////////////////////////////////////////////////
1140void bncComb::slotProviderIDChanged(QString mountPoint) {
1141 QMutexLocker locker(&_mutex);
1142
1143 // Find the AC Name
1144 // ----------------
1145 QString acName;
1146 QListIterator<cmbAC*> icAC(_ACs);
1147 while (icAC.hasNext()) {
1148 cmbAC* AC = icAC.next();
1149 if (AC->mountPoint == mountPoint) {
1150 acName = AC->name;
1151 break;
1152 }
1153 }
1154 if (acName.isEmpty()) {
1155 return;
1156 }
1157
1158 // Remove all corrections of the corresponding AC
1159 // ----------------------------------------------
1160 QListIterator<bncTime> itTime(_buffer.keys());
1161 while (itTime.hasNext()) {
1162 bncTime epoTime = itTime.next();
1163 QVector<cmbCorr*>& corrVec = _buffer[epoTime].corrs;
[5584]1164 QMutableVectorIterator<cmbCorr*> it(corrVec);
[5583]1165 while (it.hasNext()) {
1166 cmbCorr* corr = it.next();
1167 if (acName == corr->acName) {
[5584]1168 delete corr;
1169 it.remove();
[5583]1170 }
1171 }
1172 }
1173}
Note: See TracBrowser for help on using the repository browser.