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

Last change on this file since 10358 was 10330, checked in by stuerze, 12 months ago

another test is added in PPP and combination mode to check if stored ephemerides were outdated and/or not updated in between

File size: 46.4 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 *
[7297]13 * Changes:
[2898]14 *
15 * -----------------------------------------------------------------------*/
16
[2933]17#include <newmatio.h>
[2921]18#include <iomanip>
[3214]19#include <sstream>
[9635]20#include <map>
[2898]21
22#include "bnccomb.h"
[9676]23#include <combination/bncbiassnx.h>
[5070]24#include "bnccore.h"
[3201]25#include "upload/bncrtnetdecoder.h"
[2910]26#include "bncsettings.h"
[2988]27#include "bncutils.h"
[3052]28#include "bncantex.h"
[5742]29#include "t_prn.h"
[2898]30
[3455]31const double sig0_offAC = 1000.0;
32const double sig0_offACSat = 100.0;
[3468]33const double sigP_offACSat = 0.01;
[3455]34const double sig0_clkSat = 100.0;
35
36const double sigObs = 0.05;
37
38using namespace std;
39
[2898]40// Constructor
41////////////////////////////////////////////////////////////////////////////
[6155]42bncComb::cmbParam::cmbParam(parType type_, int index_, const QString& ac_, const QString& prn_) {
[3032]43
[3433]44 type = type_;
45 index = index_;
46 AC = ac_;
47 prn = prn_;
48 xx = 0.0;
[3455]49 eph = 0;
[3429]50
[9258]51 if (type == offACgnss) {
[3455]52 epoSpec = true;
53 sig0 = sig0_offAC;
54 sigP = sig0;
[3429]55 }
56 else if (type == offACSat) {
[3455]57 epoSpec = false;
58 sig0 = sig0_offACSat;
59 sigP = sigP_offACSat;
[3429]60 }
61 else if (type == clkSat) {
[3455]62 epoSpec = true;
63 sig0 = sig0_clkSat;
64 sigP = sig0;
[3429]65 }
[2933]66}
67
68// Destructor
69////////////////////////////////////////////////////////////////////////////
[6155]70bncComb::cmbParam::~cmbParam() {
[2933]71}
72
73// Partial
74////////////////////////////////////////////////////////////////////////////
[9258]75double bncComb::cmbParam::partial(char sys, const QString& AC_, const QString& prn_) {
[7297]76
[9258]77 if (type == offACgnss) {
78 if (AC == AC_ && prn_[0].toLatin1() == sys) {
[2934]79 return 1.0;
80 }
81 }
[3429]82 else if (type == offACSat) {
83 if (AC == AC_ && prn == prn_) {
[2933]84 return 1.0;
85 }
86 }
[3429]87 else if (type == clkSat) {
88 if (prn == prn_) {
[2933]89 return 1.0;
90 }
91 }
92
93 return 0.0;
94}
95
[7297]96//
[2990]97////////////////////////////////////////////////////////////////////////////
[9258]98QString bncComb::cmbParam::toString(char sys) const {
[2933]99
[2990]100 QString outStr;
[7297]101
[9258]102 if (type == offACgnss) {
[9262]103 outStr = "AC Offset " + AC + " " + sys + " ";
[2990]104 }
[3429]105 else if (type == offACSat) {
[7008]106 outStr = "Sat Offset " + AC + " " + prn.mid(0,3);
[2990]107 }
[3429]108 else if (type == clkSat) {
[7008]109 outStr = "Clk Corr " + prn.mid(0,3);
[2990]110 }
[2933]111
[2990]112 return outStr;
113}
114
[10040]115// Singleton: definition class variable
[7299]116////////////////////////////////////////////////////////////////////////////
[10042]117bncComb* bncComb::instance = nullptr;
[7299]118
[10040]119
[2933]120// Constructor
121////////////////////////////////////////////////////////////////////////////
[10040]122bncComb::bncComb() : _ephUser(true) {
[2910]123
124 bncSettings settings;
125
[10227]126 _running = true;
127
[7297]128 QStringList cmbStreams = settings.value("cmbStreams").toStringList();
[2918]129
[4183]130 _cmbSampl = settings.value("cmbSampl").toInt();
131 if (_cmbSampl <= 0) {
[9676]132 _cmbSampl = 5;
[4183]133 }
[9292]134 _useGps = (Qt::CheckState(settings.value("cmbGps").toInt()) == Qt::Checked) ? true : false;
135 if (_useGps) {
136 _cmbSysPrn['G'] = t_prn::MAXPRN_GPS;
137 _masterMissingEpochs['G'] = 0;
138 }
139 _useGlo = (Qt::CheckState(settings.value("cmbGlo").toInt()) == Qt::Checked) ? true : false;
140 if (_useGlo) {
141 _cmbSysPrn['R'] = t_prn::MAXPRN_GLONASS;
142 _masterMissingEpochs['R'] = 0;
143 }
144 _useGal = (Qt::CheckState(settings.value("cmbGal").toInt()) == Qt::Checked) ? true : false;
145 if (_useGal) {
146 _cmbSysPrn['E'] = t_prn::MAXPRN_GALILEO;
147 _masterMissingEpochs['E'] = 0;
148 }
149 _useBds = (Qt::CheckState(settings.value("cmbBds").toInt()) == Qt::Checked) ? true : false;
150 if (_useBds) {
151 _cmbSysPrn['C'] = t_prn::MAXPRN_BDS;
152 _masterMissingEpochs['C'] = 0;
153 }
154 _useQzss = (Qt::CheckState(settings.value("cmbQzss").toInt()) == Qt::Checked) ? true : false;
155 if (_useQzss) {
156 _cmbSysPrn['J'] = t_prn::MAXPRN_QZSS;
157 _masterMissingEpochs['J'] = 0;
158 }
159 _useSbas = (Qt::CheckState(settings.value("cmbSbas").toInt()) == Qt::Checked) ? true : false;
160 if (_useSbas) {
161 _cmbSysPrn['S'] = t_prn::MAXPRN_SBAS;
162 _masterMissingEpochs['S'] = 0;
163 }
164 _useIrnss = (Qt::CheckState(settings.value("cmbIrnss").toInt()) == Qt::Checked) ? true : false;
165 if (_useIrnss) {
166 _cmbSysPrn['I'] = t_prn::MAXPRN_IRNSS;
167 _masterMissingEpochs['I'] = 0;
168 }
[4183]169
[7297]170 if (cmbStreams.size() >= 1 && !cmbStreams[0].isEmpty()) {
171 QListIterator<QString> it(cmbStreams);
[2910]172 while (it.hasNext()) {
173 QStringList hlp = it.next().split(" ");
[2918]174 cmbAC* newAC = new cmbAC();
[9821]175 newAC->mountPoint = hlp[0];
176 newAC->name = hlp[1];
177 newAC->weightFactor = hlp[2].toDouble();
[10221]178 newAC->isAPC = bool(newAC->mountPoint.mid(0,4) == "SSRA");
[9258]179 QMapIterator<char, unsigned> itSys(_cmbSysPrn);
180 // init
181 while (itSys.hasNext()) {
182 itSys.next();
183 char sys = itSys.key();
[10116]184 if (!_masterOrbitAC.contains(sys)) {
[9258]185 _masterOrbitAC[sys] = newAC->name;
[10116]186 _masterIsAPC[sys] = newAC->isAPC;
[9258]187 }
[3453]188 }
[3429]189 _ACs.append(newAC);
[2910]190 }
191 }
[2927]192
[9025]193 QString ssrFormat;
[9258]194 _ssrCorr = 0;
[9025]195 QListIterator<QString> it(settings.value("uploadMountpointsOut").toStringList());
196 while (it.hasNext()) {
197 QStringList hlp = it.next().split(",");
198 if (hlp.size() > 7) {
199 ssrFormat = hlp[7];
200 }
201 }
202 if (ssrFormat == "IGS-SSR") {
203 _ssrCorr = new SsrCorrIgs();
204 }
205 else if (ssrFormat == "RTCM-SSR") {
206 _ssrCorr = new SsrCorrRtcm();
207 }
[9258]208 else { // default
209 _ssrCorr = new SsrCorrIgs();
210 }
[9025]211
[10311]212 _rtnetDecoder = new bncRtnetDecoder();
[3201]213
[9819]214 connect(this, SIGNAL(newMessage(QByteArray,bool)), BNC_CORE, SLOT(slotMessage(const QByteArray,bool)));
215 connect(BNC_CORE, SIGNAL(providerIDChanged(QString)), this, SLOT(slotProviderIDChanged(QString)));
216 connect(BNC_CORE, SIGNAL(newOrbCorrections(QList<t_orbCorr>)), this, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)));
217 connect(BNC_CORE, SIGNAL(newClkCorrections(QList<t_clkCorr>)), this, SLOT(slotNewClkCorrections(QList<t_clkCorr>)));
218 connect(BNC_CORE, SIGNAL(newCodeBiases(QList<t_satCodeBias>)), this, SLOT(slotNewCodeBiases(QList<t_satCodeBias>)));
[2933]219
[3470]220 // Combination Method
221 // ------------------
[3481]222 if (settings.value("cmbMethod").toString() == "Single-Epoch") {
223 _method = singleEpoch;
[3470]224 }
225 else {
[3481]226 _method = filter;
[3470]227 }
[3032]228
229 // Initialize Parameters (model: Clk_Corr = AC_Offset + Sat_Offset + Clk)
230 // ----------------------------------------------------------------------
[3470]231 if (_method == filter) {
[9258]232 // SYSTEM
233 QMapIterator<char, unsigned> itSys(_cmbSysPrn);
234 while (itSys.hasNext()) {
235 itSys.next();
236 int nextPar = 0;
237 char sys = itSys.key();
238 unsigned maxPrn = itSys.value();
239 unsigned flag = 0;
240 if (sys == 'E') {
241 flag = 1;
[3470]242 }
[9258]243 // AC
244 QListIterator<cmbAC*> itAc(_ACs);
245 while (itAc.hasNext()) {
246 cmbAC* AC = itAc.next();
247 _params[sys].push_back(new cmbParam(cmbParam::offACgnss, ++nextPar, AC->name, ""));
248 for (unsigned iGnss = 1; iGnss <= maxPrn; iGnss++) {
249 QString prn = QString("%1%2_%3").arg(sys).arg(iGnss, 2, 10, QChar('0')).arg(flag);
250 _params[sys].push_back(new cmbParam(cmbParam::offACSat, ++nextPar, AC->name, prn));
[3483]251 }
252 }
[9258]253 for (unsigned iGnss = 1; iGnss <= maxPrn; iGnss++) {
254 QString prn = QString("%1%2_%3").arg(sys).arg(iGnss, 2, 10, QChar('0')).arg(flag);
255 _params[sys].push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
[3483]256 }
[9258]257 // Initialize Variance-Covariance Matrix
258 // -------------------------------------
259 _QQ[sys].ReSize(_params[sys].size());
260 _QQ[sys] = 0.0;
261 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
262 cmbParam* pp = _params[sys][iPar-1];
263 _QQ[sys](iPar,iPar) = pp->sig0 * pp->sig0;
264 }
[3483]265 }
[2991]266 }
[2933]267
[3052]268 // ANTEX File
269 // ----------
270 _antex = 0;
[6667]271 QString antexFileName = settings.value("uploadAntexFile").toString();
[3052]272 if (!antexFileName.isEmpty()) {
273 _antex = new bncAntex();
274 if (_antex->readFile(antexFileName) != success) {
[9695]275 emit newMessage("bncCmb: wrong ANTEX file", true);
[3052]276 delete _antex;
277 _antex = 0;
278 }
279 }
[3138]280
[9676]281
282 // Bias SINEX File
283 // ---------------
284 _bsx = 0;
285 QString bsxFileName = settings.value("cmbBsxFile").toString();
286 if (!bsxFileName.isEmpty()) {
287 _bsx = new bncBiasSnx();
288 if (_bsx->readFile(bsxFileName) != success) {
[9695]289 emit newMessage("bncComb: wrong Bias SINEX file", true);
[9676]290 delete _bsx;
291 _bsx = 0;
292 }
293 }
294 if (_bsx) {
[10227]295 _ms = 3.0 * 3600 * 1000.0;
[9695]296 QTimer::singleShot(_ms, this, SLOT(slotReadBiasSnxFile()));
[9676]297 }
298
[3329]299 // Maximal Residuum
300 // ----------------
301 _MAXRES = settings.value("cmbMaxres").toDouble();
302 if (_MAXRES <= 0.0) {
303 _MAXRES = 999.0;
304 }
[10227]305 _newCorr = 0;
[2898]306}
307
308// Destructor
309////////////////////////////////////////////////////////////////////////////
310bncComb::~bncComb() {
[10221]311
[10227]312 _running = false;
[10275]313#ifndef WIN32
[10227]314 sleep(2);
[10275]315#else
316 Sleep(2000);
317#endif
[10227]318
[3429]319 QListIterator<cmbAC*> icAC(_ACs);
320 while (icAC.hasNext()) {
321 delete icAC.next();
[2918]322 }
[10235]323 _ACs.clear();
[10216]324
[3201]325 delete _rtnetDecoder;
[10216]326
[9025]327 if (_ssrCorr) {
328 delete _ssrCorr;
[10221]329 }
[10216]330
[10227]331 if (_newCorr) {
332 delete _newCorr;
333 }
334
[3052]335 delete _antex;
[9676]336 delete _bsx;
[10038]337
[9258]338 QMapIterator<char, unsigned> itSys(_cmbSysPrn);
339 while (itSys.hasNext()) {
340 itSys.next();
341 char sys = itSys.key();
342 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
343 delete _params[sys][iPar-1];
344 }
[10038]345 _buffer.remove(sys);
[3296]346 }
[10229]347 _params.clear();
348 _buffer.clear();
[10227]349 _cmbSysPrn.clear();
[10216]350
[10038]351 while (!_epoClkData.empty()) {
352 delete _epoClkData.front();
353 _epoClkData.pop_front();
354 }
[2898]355}
356
[9676]357void bncComb::slotReadBiasSnxFile() {
358 bncSettings settings;
359 QString bsxFileName = settings.value("cmbBsxFile").toString();
[9695]360
361 if (bsxFileName.isEmpty()) {
362 emit newMessage("bncComb: no Bias SINEX file specified", true);
363 return;
364 }
365 if (!_bsx) {
[9676]366 _bsx = new bncBiasSnx();
367 }
368 if (_bsx->readFile(bsxFileName) != success) {
[9695]369 emit newMessage("bncComb: wrong Bias SINEX file", true);
[9676]370 delete _bsx;
371 _bsx = 0;
372 }
373 else {
[9695]374 emit newMessage("bncComb: successfully read Bias SINEX file", true);
[9676]375 }
[9710]376
[9695]377 QTimer::singleShot(_ms, this, SLOT(slotReadBiasSnxFile()));
[9676]378}
379
380
[6155]381// Remember orbit corrections
[2898]382////////////////////////////////////////////////////////////////////////////
[6155]383void bncComb::slotNewOrbCorrections(QList<t_orbCorr> orbCorrections) {
[2906]384 QMutexLocker locker(&_mutex);
[6155]385 for (int ii = 0; ii < orbCorrections.size(); ii++) {
386 t_orbCorr& orbCorr = orbCorrections[ii];
387 QString staID(orbCorr._staID.c_str());
[9296]388 char sys = orbCorr._prn.system();
[6155]389
[9296]390 if (!_cmbSysPrn.contains(sys)){
391 continue;
392 }
393
[6155]394 // Find/Check the AC Name
395 // ----------------------
396 QString acName;
397 QListIterator<cmbAC*> icAC(_ACs);
398 while (icAC.hasNext()) {
399 cmbAC* AC = icAC.next();
400 if (AC->mountPoint == staID) {
401 acName = AC->name;
402 break;
403 }
[3431]404 }
[6155]405 if (acName.isEmpty()) {
406 continue;
407 }
[3431]408
[6155]409 // Store the correction
410 // --------------------
[10216]411 QMap<t_prn, t_orbCorr>& storage = _orbCorrections[acName];
412 storage[orbCorr._prn] = orbCorr;
[2913]413 }
[6155]414}
[2913]415
[9676]416// Remember Satellite Code Biases
[9529]417////////////////////////////////////////////////////////////////////////////
[9530]418void bncComb::slotNewCodeBiases(QList<t_satCodeBias> satCodeBiases) {
[9529]419 QMutexLocker locker(&_mutex);
420
[9530]421 for (int ii = 0; ii < satCodeBiases.size(); ii++) {
422 t_satCodeBias& satCodeBias = satCodeBiases[ii];
423 QString staID(satCodeBias._staID.c_str());
424 char sys = satCodeBias._prn.system();
[9529]425
426 if (!_cmbSysPrn.contains(sys)){
427 continue;
428 }
429
430 // Find/Check the AC Name
431 // ----------------------
432 QString acName;
433 QListIterator<cmbAC*> icAC(_ACs);
434 while (icAC.hasNext()) {
435 cmbAC* AC = icAC.next();
436 if (AC->mountPoint == staID) {
437 acName = AC->name;
438 break;
439 }
440 }
441 if (acName.isEmpty()) {
442 continue;
443 }
444
445 // Store the correction
446 // --------------------
[10216]447 QMap<t_prn, t_satCodeBias>& storage = _satCodeBiases[acName];
448 storage[satCodeBias._prn] = satCodeBias;
[9529]449 }
[10229]450
[9529]451}
452
[6155]453// Process clock corrections
454////////////////////////////////////////////////////////////////////////////
455void bncComb::slotNewClkCorrections(QList<t_clkCorr> clkCorrections) {
456 QMutexLocker locker(&_mutex);
[10243]457 const double outWait = 1.0 * _cmbSampl;
[10081]458 int currentWeek = 0;
459 double currentSec = 0.0;
460 currentGPSWeeks(currentWeek, currentSec);
461 bncTime currentTime(currentWeek, currentSec);
[6155]462
[10038]463 // Loop over all observations (possible different epochs)
464 // -----------------------------------------------------
[6155]465 for (int ii = 0; ii < clkCorrections.size(); ii++) {
[10225]466 t_clkCorr& newClk = clkCorrections[ii];
467 char sys = newClk._prn.system();
[9296]468 if (!_cmbSysPrn.contains(sys)){
469 continue;
470 }
471
[10038]472 // Check Modulo Time
[6155]473 // -----------------
[10225]474 int sec = int(nint(newClk._time.gpssec()*10));
[10038]475 if (sec % (_cmbSampl*10) != 0.0) {
476 continue;
[3483]477 }
478
[6155]479 // Find/Check the AC Name
480 // ----------------------
481 QString acName;
[10116]482 bool isAPC;
[10225]483 QString staID(newClk._staID.c_str());
[6155]484 QListIterator<cmbAC*> icAC(_ACs);
485 while (icAC.hasNext()) {
486 cmbAC* AC = icAC.next();
487 if (AC->mountPoint == staID) {
488 acName = AC->name;
[10116]489 isAPC = AC->isAPC;
[6155]490 break;
491 }
[3588]492 }
[10116]493 if (acName.isEmpty() || isAPC != _masterIsAPC[sys]) {
[6155]494 continue;
495 }
[3588]496
[10098]497 // Check regarding current time
498 // ----------------------------
[10192]499 if (BNC_CORE->mode() != t_bncCore::batchPostProcessing) {
[10225]500 if ((newClk._time >= currentTime) || // future time stamp
[10245]501 (currentTime - newClk._time) > 60.0) { // very old data sets
[10192]502 #ifdef BNC_DEBUG_CMB
[10225]503 emit newMessage("bncComb: future or very old data sets: " + acName.toLatin1() + " " + newClk._prn.toString().c_str() +
504 QString(" %1 ").arg(newClk._time.timestr().c_str()).toLatin1() + "vs. current time" +
[10192]505 QString(" %1 ").arg(currentTime.timestr().c_str()).toLatin1(), true);
506 #endif
507 continue;
508 }
[10098]509 }
510
[6155]511 // Check Correction Age
512 // --------------------
[10225]513 if (_resTime.valid() && newClk._time <= _resTime) {
[10077]514#ifdef BNC_DEBUG_CMB
[10225]515 emit newMessage("bncComb: old correction: " + acName.toLatin1() + " " + newClk._prn.toString().c_str() +
516 QString(" %1 ").arg(newClk._time.timestr().c_str()).toLatin1() + "vs. last processing Epoch" +
[10042]517 QString(" %1 ").arg(_resTime.timestr().c_str()).toLatin1(), true);
[10077]518#endif
[6155]519 continue;
520 }
[7297]521
[10153]522 // Set the last time
523 // -----------------
[10225]524 if (_lastClkCorrTime.undef() || newClk._time > _lastClkCorrTime) {
525 _lastClkCorrTime = newClk._time;
[10153]526 }
527
[10038]528 // Find the corresponding data epoch or create a new one
529 // -----------------------------------------------------
530 epoClkData* epoch = 0;
531 deque<epoClkData*>::const_iterator it;
532 for (it = _epoClkData.begin(); it != _epoClkData.end(); it++) {
[10225]533 if (newClk._time == (*it)->_time) {
[10038]534 epoch = *it;
535 break;
536 }
537 }
538 if (epoch == 0) {
[10225]539 if (_epoClkData.empty() || newClk._time > _epoClkData.back()->_time) {
[10038]540 epoch = new epoClkData;
[10225]541 epoch->_time = newClk._time;
[10038]542 _epoClkData.push_back(epoch);
543 }
544 }
545
546 // Put data into the epoch
547 // -----------------------
548 if (epoch != 0) {
549 epoch->_clkCorr.push_back(newClk);
550 }
551 }
552
553 // Make sure the buffer does not grow beyond any limit
554 // ---------------------------------------------------
[10200]555 const unsigned MAX_EPODATA_SIZE = 60.0 / _cmbSampl;
[10038]556 if (_epoClkData.size() > MAX_EPODATA_SIZE) {
557 delete _epoClkData.front();
558 _epoClkData.pop_front();
559 }
560
561 // Process the oldest epochs
562 // ------------------------
563 while (_epoClkData.size()) {
564
[10042]565 if (!_lastClkCorrTime.valid()) {
[10038]566 return;
567 }
568
[10225]569 const vector<t_clkCorr>& clkCorrVec = _epoClkData.front()->_clkCorr;
[10038]570
571 bncTime epoTime = _epoClkData.front()->_time;
572
[10200]573 if (BNC_CORE->mode() != t_bncCore::batchPostProcessing) {
[10245]574 if ((currentTime - epoTime) > 60.0) {
[10200]575 delete _epoClkData.front();
576 _epoClkData.pop_front();
577 continue;
578 }
[10043]579 }
[10038]580 // Process the front epoch
581 // -----------------------
[10243]582 if (epoTime < (_lastClkCorrTime - outWait)) {
[10042]583 _resTime = epoTime;
584 processEpoch(_resTime, clkCorrVec);
[10038]585 delete _epoClkData.front();
586 _epoClkData.pop_front();
587 }
588 else {
589 return;
590 }
591 }
592}
593
594// Change the correction so that it refers to last received ephemeris
595////////////////////////////////////////////////////////////////////////////
596void bncComb::switchToLastEph(t_eph* lastEph, cmbCorr* corr) {
597
598 if (corr->_eph == lastEph) {
599 return;
600 }
601
602 ColumnVector oldXC(6);
603 ColumnVector oldVV(3);
604 if (corr->_eph->getCrd(corr->_time, oldXC, oldVV, false) != success) {
605 return;
606 }
607
608 ColumnVector newXC(6);
609 ColumnVector newVV(3);
610 if (lastEph->getCrd(corr->_time, newXC, newVV, false) != success) {
611 return;
612 }
613
614 ColumnVector dX = newXC.Rows(1,3) - oldXC.Rows(1,3);
615 ColumnVector dV = newVV - oldVV;
616 double dC = newXC(4) - oldXC(4);
617
618 ColumnVector dRAO(3);
619 XYZ_to_RSW(newXC.Rows(1,3), newVV, dX, dRAO);
620
621 ColumnVector dDotRAO(3);
622 XYZ_to_RSW(newXC.Rows(1,3), newVV, dV, dDotRAO);
623
624 QString msg = "switch corr " + corr->_prn.mid(0,3)
625 + QString(" %1 -> %2 %3").arg(corr->_iod,3).arg(lastEph->IOD(),3).arg(dC*t_CST::c, 8, 'f', 4);
626
627 emit newMessage(msg.toLatin1(), false);
628
629 corr->_iod = lastEph->IOD();
630 corr->_eph = lastEph;
631
[10216]632 corr->_orbCorr._xr += dRAO;
633 corr->_orbCorr._dotXr += dDotRAO;
[10225]634 corr->_clkCorr._dClk -= dC;
[10038]635}
636
637// Process Epoch
638////////////////////////////////////////////////////////////////////////////
[10225]639void bncComb::processEpoch(bncTime epoTime, const vector<t_clkCorr>& clkCorrVec) {
[10330]640 QDateTime now = currentDateAndTimeGPS();
641 bncTime currentTime(now.toString(Qt::ISODate).toStdString());
[10038]642 for (unsigned ii = 0; ii < clkCorrVec.size(); ii++) {
[10225]643 const t_clkCorr& clkCorr = clkCorrVec[ii];
644 QString staID(clkCorr._staID.c_str());
645 QString prn(clkCorr._prn.toInternalString().c_str());
646 char sys = clkCorr._prn.system();
[10038]647
648 // Find/Check the AC Name
649 // ----------------------
650 QString acName;
651 double weigthFactor;
652 QListIterator<cmbAC*> icAC(_ACs);
653 while (icAC.hasNext()) {
654 cmbAC* AC = icAC.next();
655 if (AC->mountPoint == staID) {
656 acName = AC->name;
657 weigthFactor = AC->weightFactor;
658 break;
659 }
660 }
661
[6155]662 // Create new correction
663 // ---------------------
[10229]664 _newCorr = new cmbCorr();
[10227]665 _newCorr->_prn = prn;
666 _newCorr->_time = clkCorr._time;
667 _newCorr->_iod = clkCorr._iod;
668 _newCorr->_acName = acName;
669 _newCorr->_weightFactor = weigthFactor;
670 _newCorr->_clkCorr = clkCorr;
[6155]671
[6156]672 // Check orbit correction
673 // ----------------------
674 if (!_orbCorrections.contains(acName)) {
[10227]675 delete _newCorr; _newCorr = 0;
[6156]676 continue;
677 }
678 else {
[10216]679 QMap<t_prn, t_orbCorr>& storage = _orbCorrections[acName];
[10225]680 if (!storage.contains(clkCorr._prn) ||
[10227]681 storage[clkCorr._prn]._iod != _newCorr->_iod) {
682 delete _newCorr; _newCorr = 0;
[6156]683 continue;
684 }
685 else {
[10227]686 _newCorr->_orbCorr = storage[clkCorr._prn];
[6156]687 }
688 }
689
[6155]690 // Check the Ephemeris
691 //--------------------
[7012]692 t_eph* ephLast = _ephUser.ephLast(prn);
693 t_eph* ephPrev = _ephUser.ephPrev(prn);
[6443]694 if (ephLast == 0) {
[9676]695#ifdef BNC_DEBUG_CMB
[10225]696 emit newMessage("bncComb: eph not found for " + prn.mid(0,3).toLatin1(), true);
[9676]697#endif
[10227]698 delete _newCorr; _newCorr = 0;
[6155]699 continue;
[2986]700 }
[10330]701 else if (outDatedBcep(ephLast, currentTime) ||
702 ephLast->checkState() == t_eph::bad ||
[9268]703 ephLast->checkState() == t_eph::outdated ||
704 ephLast->checkState() == t_eph::unhealthy) {
[9676]705#ifdef BNC_DEBUG_CMB
[10225]706 emit newMessage("bncComb: ephLast not ok (checkState: " + ephLast->checkStateToString().toLatin1() + ") for " + prn.mid(0,3).toLatin1(), true);
[9676]707#endif
[10227]708 delete _newCorr; _newCorr = 0;
[9266]709 continue;
710 }
[3029]711 else {
[10227]712 if (ephLast->IOD() == _newCorr->_iod) {
713 _newCorr->_eph = ephLast;
[6155]714 }
[10330]715 else if (ephPrev &&
716 !outDatedBcep(ephPrev, currentTime) && // if not updated in storage
717 ephPrev->checkState() == t_eph::ok && // received
[10227]718 ephPrev->IOD() == _newCorr->_iod) {
719 _newCorr->_eph = ephPrev;
720 switchToLastEph(ephLast, _newCorr);
[6155]721 }
722 else {
[9676]723#ifdef BNC_DEBUG_CMB
[10225]724 emit newMessage("bncComb: eph not found for " + prn.mid(0,3).toLatin1() +
[10227]725 QString(" with IOD %1").arg(_newCorr->_iod).toLatin1(), true);
[9676]726#endif
[10227]727 delete _newCorr; _newCorr = 0;
[6155]728 continue;
729 }
[2986]730 }
[6155]731
[9635]732 // Check satellite code biases
733 // ----------------------------
[9819]734 if (_satCodeBiases.contains(acName)) {
[10216]735 QMap<t_prn, t_satCodeBias>& storage = _satCodeBiases[acName];
[10225]736 if (storage.contains(clkCorr._prn)) {
[10227]737 _newCorr->_satCodeBias = storage[clkCorr._prn];
[9676]738 QMap<t_frequency::type, double> codeBiasesRefSig;
739 for (unsigned ii = 1; ii < cmbRefSig::cIF; ii++) {
[10038]740 t_frequency::type frqType = cmbRefSig::toFreq(sys, static_cast<cmbRefSig::type>(ii));
[9635]741 char frqNum = t_frequency::toString(frqType)[1];
[10038]742 char attrib = cmbRefSig::toAttrib(sys, static_cast<cmbRefSig::type>(ii));
[9676]743 QString rnxType2ch = QString("%1%2").arg(frqNum).arg(attrib);
[10227]744 for (unsigned ii = 0; ii < _newCorr->_satCodeBias._bias.size(); ii++) {
745 const t_frqCodeBias& bias = _newCorr->_satCodeBias._bias[ii];
[9676]746 if (rnxType2ch.toStdString() == bias._rnxType2ch) {
747 codeBiasesRefSig[frqType] = bias._value;
[9635]748 }
749 }
750 }
[9819]751 if (codeBiasesRefSig.size() == 2) {
752 map<t_frequency::type, double> codeCoeff;
[10227]753 double channel = double(_newCorr->_eph->slotNum());
[9819]754 cmbRefSig::coeff(sys, cmbRefSig::cIF, channel, codeCoeff);
755 map<t_frequency::type, double>::const_iterator it;
756 for (it = codeCoeff.begin(); it != codeCoeff.end(); it++) {
757 t_frequency::type frqType = it->first;
[10227]758 _newCorr->_satCodeBiasIF += it->second * codeBiasesRefSig[frqType];
[9819]759 }
[9676]760 }
[10227]761 _newCorr->_satCodeBias._bias.clear();
[9635]762 }
763 }
764
[6155]765 // Store correction into the buffer
766 // --------------------------------
[10038]767 QVector<cmbCorr*>& corrs = _buffer[sys].corrs;
768 QVectorIterator<cmbCorr*> itCorr(corrs);
769 bool available = false;
770 while (itCorr.hasNext()) {
771 cmbCorr* corr = itCorr.next();
772 QString prn = corr->_prn;
773 QString acName = corr->_acName;
[10229]774 if (_newCorr->_acName == acName &&
775 _newCorr->_prn == prn) {
[10038]776 available = true;
777 }
778 }
[10227]779
[10038]780 if (!available) {
[10227]781 corrs.push_back(_newCorr); _newCorr = 0;
[10038]782 }
783 else {
[10227]784 delete _newCorr; _newCorr = 0;
[10038]785 continue;
786 }
[2986]787 }
788
[10038]789 // Process Systems of this Epoch
790 // ------------------------------
[9258]791 QMapIterator<char, unsigned> itSys(_cmbSysPrn);
792 while (itSys.hasNext()) {
793 itSys.next();
794 char sys = itSys.key();
[10038]795 _log.clear();
796 QTextStream out(&_log, QIODevice::WriteOnly);
797 processSystem(epoTime, sys, out);
[10224]798 _buffer.remove(sys);
[10038]799 emit newMessage(_log, false);
[2927]800 }
[2898]801}
802
[10038]803void bncComb::processSystem(bncTime epoTime, char sys, QTextStream& out) {
[3028]804
[9635]805 out << "\n" << "Combination: " << sys << "\n"
806 << "--------------------------------" << "\n";
[2990]807
[3433]808 // Observation Statistics
809 // ----------------------
[3455]810 bool masterPresent = false;
[3433]811 QListIterator<cmbAC*> icAC(_ACs);
812 while (icAC.hasNext()) {
813 cmbAC* AC = icAC.next();
[9258]814 AC->numObs[sys] = 0;
815 QVectorIterator<cmbCorr*> itCorr(corrs(sys));
[3433]816 while (itCorr.hasNext()) {
817 cmbCorr* corr = itCorr.next();
[6157]818 if (corr->_acName == AC->name) {
[9258]819 AC->numObs[sys] += 1;
820 if (AC->name == _masterOrbitAC[sys]) {
[3453]821 masterPresent = true;
822 }
[3433]823 }
824 }
[9635]825 out << AC->name.toLatin1().data() << ": " << AC->numObs[sys] << "\n";
[3433]826 }
827
[3453]828 // If Master not present, switch to another one
829 // --------------------------------------------
[4889]830 const unsigned switchMasterAfterGap = 1;
[3456]831 if (masterPresent) {
[9258]832 _masterMissingEpochs[sys] = 0;
[3456]833 }
834 else {
[9258]835 ++_masterMissingEpochs[sys];
836 if (_masterMissingEpochs[sys] < switchMasterAfterGap) {
[9635]837 out << "Missing Master, Epoch skipped" << "\n";
[10038]838 _buffer.remove(sys);
[3455]839 emit newMessage(_log, false);
840 return;
[3453]841 }
[3455]842 else {
[9258]843 _masterMissingEpochs[sys] = 0;
[3455]844 QListIterator<cmbAC*> icAC(_ACs);
845 while (icAC.hasNext()) {
846 cmbAC* AC = icAC.next();
[9258]847 if (AC->numObs[sys] > 0) {
[3455]848 out << "Switching Master AC "
[9258]849 << _masterOrbitAC[sys].toLatin1().data() << " --> "
[8204]850 << AC->name.toLatin1().data() << " "
[10038]851 << epoTime.datestr().c_str() << " "
852 << epoTime.timestr().c_str() << "\n";
[9258]853 _masterOrbitAC[sys] = AC->name;
[10116]854 _masterIsAPC[sys] = AC->isAPC;
[3455]855 break;
856 }
[3452]857 }
858 }
859 }
860
[6157]861 QMap<QString, cmbCorr*> resCorr;
[3472]862
863 // Perform the actual Combination using selected Method
864 // ----------------------------------------------------
865 t_irc irc;
[3475]866 ColumnVector dx;
[3472]867 if (_method == filter) {
[10038]868 irc = processEpoch_filter(epoTime, sys, out, resCorr, dx);
[3472]869 }
870 else {
[10038]871 irc = processEpoch_singleEpoch(epoTime, sys, out, resCorr, dx);
[3472]872 }
[10230]873
[3475]874 // Update Parameter Values, Print Results
875 // --------------------------------------
[3472]876 if (irc == success) {
[9258]877 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
878 cmbParam* pp = _params[sys][iPar-1];
[3475]879 pp->xx += dx(iPar);
880 if (pp->type == cmbParam::clkSat) {
881 if (resCorr.find(pp->prn) != resCorr.end()) {
[9676]882 // set clock result
[6160]883 resCorr[pp->prn]->_dClkResult = pp->xx / t_CST::c;
[9676]884 // Add Code Biases from SINEX File
885 if (_bsx) {
886 map<t_frequency::type, double> codeCoeff;
887 double channel = double(resCorr[pp->prn]->_eph->slotNum());
888 cmbRefSig::coeff(sys, cmbRefSig::cIF, channel, codeCoeff);
889 t_frequency::type fType1 = cmbRefSig::toFreq(sys, cmbRefSig::c1);
890 t_frequency::type fType2 = cmbRefSig::toFreq(sys, cmbRefSig::c2);
891 _bsx->determineSsrSatCodeBiases(pp->prn.mid(0,3), codeCoeff[fType1], codeCoeff[fType2], resCorr[pp->prn]->_satCodeBias);
892 }
[3475]893 }
894 }
[10038]895 out << epoTime.datestr().c_str() << " "
896 << epoTime.timestr().c_str() << " ";
[3475]897 out.setRealNumberNotation(QTextStream::FixedNotation);
898 out.setFieldWidth(8);
899 out.setRealNumberPrecision(4);
[9258]900 out << pp->toString(sys) << " "
[9635]901 << pp->xx << " +- " << sqrt(_QQ[sys](pp->index,pp->index)) << "\n";
[3475]902 out.setFieldWidth(0);
903 }
[10038]904 printResults(epoTime, out, resCorr);
905 dumpResults(epoTime, resCorr);
[3472]906 }
907}
908
909// Process Epoch - Filter Method
910////////////////////////////////////////////////////////////////////////////
[10038]911t_irc bncComb::processEpoch_filter(bncTime epoTime, char sys, QTextStream& out,
[6157]912 QMap<QString, cmbCorr*>& resCorr,
[3475]913 ColumnVector& dx) {
[3472]914
[3429]915 // Prediction Step
916 // ---------------
[9258]917 int nPar = _params[sys].size();
[2933]918 ColumnVector x0(nPar);
[9258]919 for (int iPar = 1; iPar <= nPar; iPar++) {
920 cmbParam* pp = _params[sys][iPar-1];
[3455]921 if (pp->epoSpec) {
[3451]922 pp->xx = 0.0;
[9258]923 _QQ[sys].Row(iPar) = 0.0;
924 _QQ[sys].Column(iPar) = 0.0;
925 _QQ[sys](iPar,iPar) = pp->sig0 * pp->sig0;
[3451]926 }
[3455]927 else {
[9258]928 _QQ[sys](iPar,iPar) += pp->sigP * pp->sigP;
[3455]929 }
[2933]930 x0(iPar) = pp->xx;
931 }
932
[3487]933 // Check Satellite Positions for Outliers
934 // --------------------------------------
[10038]935 if (checkOrbits(epoTime, sys, out) != success) {
[3487]936 return failure;
937 }
[3441]938
[3455]939 // Update and outlier detection loop
940 // ---------------------------------
[9258]941 SymmetricMatrix QQ_sav = _QQ[sys];
[10227]942 while (_running) {
[3135]943
[3455]944 Matrix AA;
945 ColumnVector ll;
946 DiagonalMatrix PP;
[3429]947
[9258]948 if (createAmat(sys, AA, ll, PP, x0, resCorr) != success) {
[3472]949 return failure;
[3441]950 }
[2933]951
[6169]952 dx.ReSize(nPar); dx = 0.0;
[9258]953 kalman(AA, ll, PP, _QQ[sys], dx);
[6164]954
[3429]955 ColumnVector vv = ll - AA * dx;
[3080]956
[3429]957 int maxResIndex;
[7297]958 double maxRes = vv.maximum_absolute_value1(maxResIndex);
[3429]959 out.setRealNumberNotation(QTextStream::FixedNotation);
[7297]960 out.setRealNumberPrecision(3);
[10038]961 out << epoTime.datestr().c_str() << " " << epoTime.timestr().c_str()
[3432]962 << " Maximum Residuum " << maxRes << ' '
[9258]963 << corrs(sys)[maxResIndex-1]->_acName << ' ' << corrs(sys)[maxResIndex-1]->_prn.mid(0,3);
[3432]964 if (maxRes > _MAXRES) {
[9258]965 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
966 cmbParam* pp = _params[sys][iPar-1];
[7297]967 if (pp->type == cmbParam::offACSat &&
[9258]968 pp->AC == corrs(sys)[maxResIndex-1]->_acName &&
969 pp->prn == corrs(sys)[maxResIndex-1]->_prn.mid(0,3)) {
[3432]970 QQ_sav.Row(iPar) = 0.0;
971 QQ_sav.Column(iPar) = 0.0;
[3455]972 QQ_sav(iPar,iPar) = pp->sig0 * pp->sig0;
[3432]973 }
974 }
975
[9635]976 out << " Outlier" << "\n";
[9258]977 _QQ[sys] = QQ_sav;
[10230]978 delete corrs(sys)[maxResIndex-1];
[9258]979 corrs(sys).remove(maxResIndex-1);
[3432]980 }
981 else {
[9635]982 out << " OK" << "\n";
[6330]983 out.setRealNumberNotation(QTextStream::FixedNotation);
984 out.setRealNumberPrecision(4);
[9258]985 for (int ii = 0; ii < corrs(sys).size(); ii++) {
986 const cmbCorr* corr = corrs(sys)[ii];
[10038]987 out << epoTime.datestr().c_str() << ' '
988 << epoTime.timestr().c_str() << " "
[6962]989 << corr->_acName << ' ' << corr->_prn.mid(0,3);
[6330]990 out.setFieldWidth(10);
[9635]991 out << " res = " << vv[ii] << "\n";
[6330]992 out.setFieldWidth(0);
993 }
[3432]994 break;
995 }
996 }
997
[3472]998 return success;
[2918]999}
[3011]1000
[3201]1001// Print results
[3011]1002////////////////////////////////////////////////////////////////////////////
[10038]1003void bncComb::printResults(bncTime epoTime, QTextStream& out,
[6157]1004 const QMap<QString, cmbCorr*>& resCorr) {
[3011]1005
[6157]1006 QMapIterator<QString, cmbCorr*> it(resCorr);
[3011]1007 while (it.hasNext()) {
1008 it.next();
[6157]1009 cmbCorr* corr = it.value();
1010 const t_eph* eph = corr->_eph;
[3015]1011 if (eph) {
[8542]1012 ColumnVector xc(6);
[6109]1013 ColumnVector vv(3);
[10038]1014 if (eph->getCrd(epoTime, xc, vv, false) != success) {
[9268]1015 continue;
1016 }
[10038]1017 out << epoTime.datestr().c_str() << " "
1018 << epoTime.timestr().c_str() << " ";
[3015]1019 out.setFieldWidth(3);
[6962]1020 out << "Full Clock " << corr->_prn.mid(0,3) << " " << corr->_iod << " ";
[3015]1021 out.setFieldWidth(14);
[9635]1022 out << (xc(4) + corr->_dClkResult) * t_CST::c << "\n";
[3370]1023 out.setFieldWidth(0);
[3015]1024 }
1025 else {
[9691]1026 out << "bncComb::printResults bug" << "\n";
[3015]1027 }
[9676]1028 out.flush();
[3011]1029 }
1030}
[3202]1031
1032// Send results to RTNet Decoder and directly to PPP Client
1033////////////////////////////////////////////////////////////////////////////
[10229]1034void bncComb::dumpResults(bncTime epoTime, QMap<QString, cmbCorr*>& resCorr) {
[3202]1035
[6161]1036 QList<t_orbCorr> orbCorrections;
1037 QList<t_clkCorr> clkCorrections;
[9676]1038 QList<t_satCodeBias> satCodeBiasList;
[6161]1039
[3214]1040 unsigned year, month, day, hour, minute;
1041 double sec;
[10038]1042 epoTime.civil_date(year, month, day);
1043 epoTime.civil_time(hour, minute, sec);
[3214]1044
[9676]1045 QString outLines = QString().asprintf("* %4d %2d %2d %d %d %12.8f\n",
1046 year, month, day, hour, minute, sec);
[3214]1047
[10229]1048 QMutableMapIterator<QString, cmbCorr*> it(resCorr);
[3202]1049 while (it.hasNext()) {
1050 it.next();
[6157]1051 cmbCorr* corr = it.value();
[10038]1052
[9676]1053 // ORBIT
[10216]1054 t_orbCorr orbCorr(corr->_orbCorr);
[7013]1055 orbCorr._staID = "INTERNAL";
1056 orbCorrections.push_back(orbCorr);
[10038]1057
[9676]1058 // CLOCK
[10225]1059 t_clkCorr clkCorr(corr->_clkCorr);
[7013]1060 clkCorr._staID = "INTERNAL";
1061 clkCorr._dClk = corr->_dClkResult;
1062 clkCorr._dotDClk = 0.0;
1063 clkCorr._dotDotDClk = 0.0;
1064 clkCorrections.push_back(clkCorr);
[10038]1065
[8542]1066 ColumnVector xc(6);
[4978]1067 ColumnVector vv(3);
[7013]1068 corr->_eph->setClkCorr(dynamic_cast<const t_clkCorr*>(&clkCorr));
1069 corr->_eph->setOrbCorr(dynamic_cast<const t_orbCorr*>(&orbCorr));
[10038]1070 if (corr->_eph->getCrd(epoTime, xc, vv, true) != success) {
[9268]1071 delete corr;
[10229]1072 it.remove();
[9268]1073 continue;
1074 }
[7012]1075
[4978]1076 // Correction Phase Center --> CoM
1077 // -------------------------------
[10153]1078 ColumnVector dx(3); dx = 0.0;
[10116]1079 ColumnVector apc(3); apc = 0.0;
1080 ColumnVector com(3); com = 0.0;
1081 bool masterIsAPC = true;
[4978]1082 if (_antex) {
[10038]1083 double Mjd = epoTime.mjd() + epoTime.daysec()/86400.0;
[10116]1084 char sys = corr->_eph->prn().system();
1085 masterIsAPC = _masterIsAPC[sys];
[6157]1086 if (_antex->satCoMcorrection(corr->_prn, Mjd, xc.Rows(1,3), dx) != success) {
[4992]1087 dx = 0;
[8204]1088 _log += "antenna not found " + corr->_prn.mid(0,3).toLatin1() + '\n';
[3214]1089 }
1090 }
[10116]1091 if (masterIsAPC) {
1092 apc(1) = xc(1);
1093 apc(2) = xc(2);
1094 apc(3) = xc(3);
1095 com(1) = xc(1)-dx(1);
1096 com(2) = xc(2)-dx(2);
1097 com(3) = xc(3)-dx(3);
1098 }
1099 else {
1100 com(1) = xc(1);
1101 com(2) = xc(2);
1102 com(3) = xc(3);
1103 apc(1) = xc(1)+dx(1);
1104 apc(2) = xc(2)+dx(2);
1105 apc(3) = xc(3)+dx(3);
1106 }
[7012]1107
1108 outLines += corr->_prn.mid(0,3);
[10109]1109 QString hlp = QString().asprintf(
1110 " APC 3 %15.4f %15.4f %15.4f"
[9676]1111 " Clk 1 %15.4f"
1112 " Vel 3 %15.4f %15.4f %15.4f"
1113 " CoM 3 %15.4f %15.4f %15.4f",
[10116]1114 apc(1), apc(2), apc(3),
[9676]1115 xc(4) * t_CST::c,
1116 vv(1), vv(2), vv(3),
[10116]1117 com(1), com(2), com(3));
[5337]1118 outLines += hlp;
[9676]1119 hlp.clear();
[5337]1120
[10101]1121 // CODE BIASES
[10216]1122 if (corr->_satCodeBias._bias.size()) {
1123 t_satCodeBias satCodeBias(corr->_satCodeBias);
[10101]1124 satCodeBias._staID = "INTERNAL";
1125 satCodeBiasList.push_back(satCodeBias);
[9676]1126 hlp = QString().asprintf(" CodeBias %2lu", satCodeBias._bias.size());
1127 outLines += hlp;
1128 hlp.clear();
1129 for (unsigned ii = 0; ii < satCodeBias._bias.size(); ii++) {
1130 const t_frqCodeBias& frqCodeBias = satCodeBias._bias[ii];
1131 if (!frqCodeBias._rnxType2ch.empty()) {
1132 hlp = QString().asprintf(" %s%10.6f", frqCodeBias._rnxType2ch.c_str(), frqCodeBias._value);
1133 outLines += hlp;
1134 hlp.clear();
1135 }
1136 }
1137 }
1138 outLines += "\n";
[3214]1139 delete corr;
[10229]1140 it.remove();
[3214]1141 }
1142
[5337]1143 outLines += "EOE\n"; // End Of Epoch flag
[9710]1144 //cout << outLines.toStdString();
[5337]1145
[3215]1146 vector<string> errmsg;
[8204]1147 _rtnetDecoder->Decode(outLines.toLatin1().data(), outLines.length(), errmsg);
[3215]1148
[5861]1149 // Send new Corrections to PPP etc.
1150 // --------------------------------
[6161]1151 if (orbCorrections.size() > 0 && clkCorrections.size() > 0) {
1152 emit newOrbCorrections(orbCorrections);
1153 emit newClkCorrections(clkCorrections);
1154 }
[9676]1155 if (satCodeBiasList.size()) {
1156 emit newCodeBiases(satCodeBiasList);
1157 }
[3202]1158}
[3455]1159
1160// Create First Design Matrix and Vector of Measurements
1161////////////////////////////////////////////////////////////////////////////
[9258]1162t_irc bncComb::createAmat(char sys, Matrix& AA, ColumnVector& ll, DiagonalMatrix& PP,
1163 const ColumnVector& x0, QMap<QString, cmbCorr*>& resCorr) {
[3455]1164
[9258]1165 unsigned nPar = _params[sys].size();
1166 unsigned nObs = corrs(sys).size();
[3455]1167
1168 if (nObs == 0) {
1169 return failure;
1170 }
1171
[9258]1172 int maxSat = _cmbSysPrn[sys];
[3455]1173
[9259]1174 const int nCon = (_method == filter) ? 1 + maxSat : 0;
[3483]1175
[3455]1176 AA.ReSize(nObs+nCon, nPar); AA = 0.0;
1177 ll.ReSize(nObs+nCon); ll = 0.0;
1178 PP.ReSize(nObs+nCon); PP = 1.0 / (sigObs * sigObs);
1179
1180 int iObs = 0;
[9258]1181 QVectorIterator<cmbCorr*> itCorr(corrs(sys));
[3455]1182 while (itCorr.hasNext()) {
1183 cmbCorr* corr = itCorr.next();
[6157]1184 QString prn = corr->_prn;
[3487]1185
[3455]1186 ++iObs;
1187
[9258]1188 if (corr->_acName == _masterOrbitAC[sys] && resCorr.find(prn) == resCorr.end()) {
[6157]1189 resCorr[prn] = new cmbCorr(*corr);
[3455]1190 }
1191
[9258]1192 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
1193 cmbParam* pp = _params[sys][iPar-1];
1194 AA(iObs, iPar) = pp->partial(sys, corr->_acName, prn);
[3455]1195 }
1196
[10225]1197 ll(iObs) = (corr->_clkCorr._dClk * t_CST::c - corr->_satCodeBiasIF) - DotProduct(AA.Row(iObs), x0);
[9819]1198
[9821]1199 PP(iObs, iObs) *= 1.0 / (corr->_weightFactor * corr->_weightFactor);
[3455]1200 }
1201
1202 // Regularization
1203 // --------------
[3474]1204 if (_method == filter) {
1205 const double Ph = 1.e6;
1206 PP(nObs+1) = Ph;
[9258]1207 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
1208 cmbParam* pp = _params[sys][iPar-1];
[3465]1209 if ( AA.Column(iPar).maximum_absolute_value() > 0.0 &&
[3474]1210 pp->type == cmbParam::clkSat ) {
1211 AA(nObs+1, iPar) = 1.0;
[3455]1212 }
1213 }
[9258]1214 unsigned flag = 0;
1215 if (sys == 'E') {
1216 flag = 1;
1217 }
[10026]1218// if (sys == 'R') {
1219// return success;
1220// }
[9259]1221 int iCond = 1;
1222 // GNSS
[9258]1223 for (unsigned iGnss = 1; iGnss <= _cmbSysPrn[sys]; iGnss++) {
1224 QString prn = QString("%1%2_%3").arg(sys).arg(iGnss, 2, 10, QChar('0')).arg(flag);
[3474]1225 ++iCond;
1226 PP(nObs+iCond) = Ph;
[9258]1227 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
1228 cmbParam* pp = _params[sys][iPar-1];
[7299]1229 if ( pp &&
1230 AA.Column(iPar).maximum_absolute_value() > 0.0 &&
[7297]1231 pp->type == cmbParam::offACSat &&
[3474]1232 pp->prn == prn) {
1233 AA(nObs+iCond, iPar) = 1.0;
1234 }
1235 }
1236 }
[3455]1237 }
1238
1239 return success;
1240}
[3470]1241
1242// Process Epoch - Single-Epoch Method
1243////////////////////////////////////////////////////////////////////////////
[10038]1244t_irc bncComb::processEpoch_singleEpoch(bncTime epoTime, char sys, QTextStream& out,
[6157]1245 QMap<QString, cmbCorr*>& resCorr,
[3475]1246 ColumnVector& dx) {
[3470]1247
[3487]1248 // Check Satellite Positions for Outliers
1249 // --------------------------------------
[10038]1250 if (checkOrbits(epoTime, sys, out) != success) {
[3487]1251 return failure;
1252 }
1253
[3482]1254 // Outlier Detection Loop
1255 // ----------------------
[10227]1256 while (_running) {
[7297]1257
[3482]1258 // Remove Satellites that are not in Master
1259 // ----------------------------------------
[9258]1260 QMutableVectorIterator<cmbCorr*> it(corrs(sys));
[3482]1261 while (it.hasNext()) {
1262 cmbCorr* corr = it.next();
[6157]1263 QString prn = corr->_prn;
[3482]1264 bool foundMaster = false;
[9258]1265 QVectorIterator<cmbCorr*> itHlp(corrs(sys));
[3482]1266 while (itHlp.hasNext()) {
1267 cmbCorr* corrHlp = itHlp.next();
[6157]1268 QString prnHlp = corrHlp->_prn;
1269 QString ACHlp = corrHlp->_acName;
[9258]1270 if (ACHlp == _masterOrbitAC[sys] && prn == prnHlp) {
[3482]1271 foundMaster = true;
1272 break;
1273 }
[3476]1274 }
[3482]1275 if (!foundMaster) {
[3556]1276 delete corr;
[3482]1277 it.remove();
1278 }
[3473]1279 }
[7297]1280
[3482]1281 // Count Number of Observations per Satellite and per AC
1282 // -----------------------------------------------------
1283 QMap<QString, int> numObsPrn;
1284 QMap<QString, int> numObsAC;
[9258]1285 QVectorIterator<cmbCorr*> itCorr(corrs(sys));
[3482]1286 while (itCorr.hasNext()) {
1287 cmbCorr* corr = itCorr.next();
[6157]1288 QString prn = corr->_prn;
1289 QString AC = corr->_acName;
[3482]1290 if (numObsPrn.find(prn) == numObsPrn.end()) {
1291 numObsPrn[prn] = 1;
1292 }
1293 else {
1294 numObsPrn[prn] += 1;
1295 }
1296 if (numObsAC.find(AC) == numObsAC.end()) {
1297 numObsAC[AC] = 1;
1298 }
1299 else {
1300 numObsAC[AC] += 1;
1301 }
[3476]1302 }
[7297]1303
[9258]1304 // Clean-Up the Parameters
1305 // -----------------------
1306 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
1307 delete _params[sys][iPar-1];
[3474]1308 }
[9258]1309 _params[sys].clear();
[7297]1310
[3482]1311 // Set new Parameters
1312 // ------------------
1313 int nextPar = 0;
[7297]1314
[3482]1315 QMapIterator<QString, int> itAC(numObsAC);
1316 while (itAC.hasNext()) {
1317 itAC.next();
1318 const QString& AC = itAC.key();
1319 int numObs = itAC.value();
[9258]1320 if (AC != _masterOrbitAC[sys] && numObs > 0) {
1321 _params[sys].push_back(new cmbParam(cmbParam::offACgnss, ++nextPar, AC, ""));
[3482]1322 }
[7297]1323 }
1324
[3482]1325 QMapIterator<QString, int> itPrn(numObsPrn);
1326 while (itPrn.hasNext()) {
1327 itPrn.next();
1328 const QString& prn = itPrn.key();
1329 int numObs = itPrn.value();
1330 if (numObs > 0) {
[9258]1331 _params[sys].push_back(new cmbParam(cmbParam::clkSat, ++nextPar, "", prn));
[3482]1332 }
[7297]1333 }
1334
[9258]1335 int nPar = _params[sys].size();
[7297]1336 ColumnVector x0(nPar);
[3482]1337 x0 = 0.0;
[7297]1338
[3482]1339 // Create First-Design Matrix
1340 // --------------------------
1341 Matrix AA;
1342 ColumnVector ll;
1343 DiagonalMatrix PP;
[9258]1344 if (createAmat(sys, AA, ll, PP, x0, resCorr) != success) {
[3482]1345 return failure;
[3476]1346 }
[7297]1347
[3482]1348 ColumnVector vv;
1349 try {
1350 Matrix ATP = AA.t() * PP;
1351 SymmetricMatrix NN; NN << ATP * AA;
1352 ColumnVector bb = ATP * ll;
[9258]1353 _QQ[sys] = NN.i();
1354 dx = _QQ[sys] * bb;
[3482]1355 vv = ll - AA * dx;
[3476]1356 }
[3482]1357 catch (Exception& exc) {
[9635]1358 out << exc.what() << "\n";
[3482]1359 return failure;
[3476]1360 }
[3474]1361
[3482]1362 int maxResIndex;
[7297]1363 double maxRes = vv.maximum_absolute_value1(maxResIndex);
[3482]1364 out.setRealNumberNotation(QTextStream::FixedNotation);
[7297]1365 out.setRealNumberPrecision(3);
[10038]1366 out << epoTime.datestr().c_str() << " " << epoTime.timestr().c_str()
[3482]1367 << " Maximum Residuum " << maxRes << ' '
[9258]1368 << corrs(sys)[maxResIndex-1]->_acName << ' ' << corrs(sys)[maxResIndex-1]->_prn.mid(0,3);
[3474]1369
[3482]1370 if (maxRes > _MAXRES) {
[9635]1371 out << " Outlier" << "\n";
[9258]1372 delete corrs(sys)[maxResIndex-1];
1373 corrs(sys).remove(maxResIndex-1);
[3474]1374 }
[3482]1375 else {
[9635]1376 out << " OK" << "\n";
[3482]1377 out.setRealNumberNotation(QTextStream::FixedNotation);
[7297]1378 out.setRealNumberPrecision(3);
[3482]1379 for (int ii = 0; ii < vv.Nrows(); ii++) {
[9258]1380 const cmbCorr* corr = corrs(sys)[ii];
[10038]1381 out << epoTime.datestr().c_str() << ' '
1382 << epoTime.timestr().c_str() << " "
[6962]1383 << corr->_acName << ' ' << corr->_prn.mid(0,3);
[3482]1384 out.setFieldWidth(6);
[9635]1385 out << " res = " << vv[ii] << "\n";
[3482]1386 out.setFieldWidth(0);
1387 }
1388 return success;
[3474]1389 }
1390
[3473]1391 }
[3474]1392
[3482]1393 return failure;
[3470]1394}
[3487]1395
1396// Check Satellite Positions for Outliers
1397////////////////////////////////////////////////////////////////////////////
[10038]1398t_irc bncComb::checkOrbits(bncTime epoTime, char sys, QTextStream& out) {
[3487]1399
[10153]1400 const double MAX_DISPLACEMENT_INIT = 0.50;
[3488]1401
[10153]1402 double MAX_DISPLACEMENT = MAX_DISPLACEMENT_INIT;
1403 if (sys == 'C') {
1404 MAX_DISPLACEMENT *= 10.0;
1405 }
1406 if (sys == 'R') {
1407 MAX_DISPLACEMENT *= 2.0;
1408 }
1409
[3502]1410 // Switch to last ephemeris (if possible)
1411 // --------------------------------------
[9258]1412 QMutableVectorIterator<cmbCorr*> im(corrs(sys));
[3501]1413 while (im.hasNext()) {
1414 cmbCorr* corr = im.next();
[6157]1415 QString prn = corr->_prn;
[6443]1416
[7012]1417 t_eph* ephLast = _ephUser.ephLast(prn);
1418 t_eph* ephPrev = _ephUser.ephPrev(prn);
[6443]1419
1420 if (ephLast == 0) {
[9635]1421 out << "checkOrbit: missing eph (not found) " << corr->_prn.mid(0,3) << "\n";
[3556]1422 delete corr;
[3501]1423 im.remove();
1424 }
[6157]1425 else if (corr->_eph == 0) {
[9635]1426 out << "checkOrbit: missing eph (zero) " << corr->_prn.mid(0,3) << "\n";
[3556]1427 delete corr;
[3503]1428 im.remove();
1429 }
[3502]1430 else {
[6443]1431 if ( corr->_eph == ephLast || corr->_eph == ephPrev ) {
1432 switchToLastEph(ephLast, corr);
[3502]1433 }
1434 else {
[9635]1435 out << "checkOrbit: missing eph (deleted) " << corr->_prn.mid(0,3) << "\n";
[3556]1436 delete corr;
[3502]1437 im.remove();
1438 }
1439 }
[3501]1440 }
1441
[10227]1442 while (_running) {
[3488]1443
1444 // Compute Mean Corrections for all Satellites
1445 // -------------------------------------------
[3489]1446 QMap<QString, int> numCorr;
[3488]1447 QMap<QString, ColumnVector> meanRao;
[9258]1448 QVectorIterator<cmbCorr*> it(corrs(sys));
[3488]1449 while (it.hasNext()) {
1450 cmbCorr* corr = it.next();
[6157]1451 QString prn = corr->_prn;
[3488]1452 if (meanRao.find(prn) == meanRao.end()) {
1453 meanRao[prn].ReSize(4);
[10216]1454 meanRao[prn].Rows(1,3) = corr->_orbCorr._xr;
[7297]1455 meanRao[prn](4) = 1;
[3488]1456 }
1457 else {
[10216]1458 meanRao[prn].Rows(1,3) += corr->_orbCorr._xr;
[7297]1459 meanRao[prn](4) += 1;
[3488]1460 }
[3489]1461 if (numCorr.find(prn) == numCorr.end()) {
1462 numCorr[prn] = 1;
1463 }
1464 else {
1465 numCorr[prn] += 1;
1466 }
[3487]1467 }
[7297]1468
[9258]1469 // Compute Differences wrt. Mean, find Maximum
1470 // -------------------------------------------
[3488]1471 QMap<QString, cmbCorr*> maxDiff;
1472 it.toFront();
1473 while (it.hasNext()) {
1474 cmbCorr* corr = it.next();
[6157]1475 QString prn = corr->_prn;
[3488]1476 if (meanRao[prn](4) != 0) {
1477 meanRao[prn] /= meanRao[prn](4);
1478 meanRao[prn](4) = 0;
1479 }
[10216]1480 corr->_diffRao = corr->_orbCorr._xr - meanRao[prn].Rows(1,3);
[3488]1481 if (maxDiff.find(prn) == maxDiff.end()) {
1482 maxDiff[prn] = corr;
1483 }
1484 else {
[8901]1485 double normMax = maxDiff[prn]->_diffRao.NormFrobenius();
1486 double norm = corr->_diffRao.NormFrobenius();
[3488]1487 if (norm > normMax) {
1488 maxDiff[prn] = corr;
1489 }
[7297]1490 }
[3487]1491 }
[7297]1492
[3655]1493 if (_ACs.size() == 1) {
1494 break;
1495 }
1496
[3488]1497 // Remove Outliers
1498 // ---------------
1499 bool removed = false;
[9258]1500 QMutableVectorIterator<cmbCorr*> im(corrs(sys));
[3488]1501 while (im.hasNext()) {
1502 cmbCorr* corr = im.next();
[6157]1503 QString prn = corr->_prn;
[3489]1504 if (numCorr[prn] < 2) {
[3556]1505 delete corr;
[3489]1506 im.remove();
1507 }
1508 else if (corr == maxDiff[prn]) {
[8901]1509 double norm = corr->_diffRao.NormFrobenius();
[3488]1510 if (norm > MAX_DISPLACEMENT) {
[10038]1511 out << epoTime.datestr().c_str() << " "
1512 << epoTime.timestr().c_str() << " "
[7297]1513 << "Orbit Outlier: "
[8204]1514 << corr->_acName.toLatin1().data() << " "
1515 << prn.mid(0,3).toLatin1().data() << " "
[7297]1516 << corr->_iod << " "
[9635]1517 << norm << "\n";
[3556]1518 delete corr;
[3488]1519 im.remove();
1520 removed = true;
1521 }
1522 }
[3487]1523 }
[7297]1524
[3488]1525 if (!removed) {
1526 break;
1527 }
[3487]1528 }
1529
1530 return success;
1531}
[3588]1532
[7297]1533//
[3588]1534////////////////////////////////////////////////////////////////////////////
[5583]1535void bncComb::slotProviderIDChanged(QString mountPoint) {
1536 QMutexLocker locker(&_mutex);
1537
[8065]1538 QTextStream out(&_log, QIODevice::WriteOnly);
1539
[5583]1540 // Find the AC Name
1541 // ----------------
1542 QString acName;
1543 QListIterator<cmbAC*> icAC(_ACs);
1544 while (icAC.hasNext()) {
[10038]1545 cmbAC *AC = icAC.next();
[5583]1546 if (AC->mountPoint == mountPoint) {
1547 acName = AC->name;
[10038]1548 out << "Provider ID changed: AC " << AC->name.toLatin1().data() << " "
[10081]1549 << _resTime.datestr().c_str() << " " << _resTime.timestr().c_str()
[10038]1550 << "\n";
[5583]1551 break;
1552 }
1553 }
1554 if (acName.isEmpty()) {
1555 return;
1556 }
[9258]1557 QMapIterator<char, unsigned> itSys(_cmbSysPrn);
1558 while (itSys.hasNext()) {
1559 itSys.next();
1560 char sys = itSys.key();
1561 // Remove all corrections of the corresponding AC
1562 // ----------------------------------------------
[10038]1563 QVector<cmbCorr*> &corrVec = _buffer[sys].corrs;
1564 QMutableVectorIterator<cmbCorr*> it(corrVec);
1565 while (it.hasNext()) {
1566 cmbCorr *corr = it.next();
1567 if (acName == corr->_acName) {
1568 delete corr;
1569 it.remove();
[5583]1570 }
1571 }
[9258]1572 // Reset Satellite Offsets
1573 // -----------------------
1574 if (_method == filter) {
1575 for (int iPar = 1; iPar <= _params[sys].size(); iPar++) {
[10038]1576 cmbParam *pp = _params[sys][iPar - 1];
[9258]1577 if (pp->AC == acName && pp->type == cmbParam::offACSat) {
1578 pp->xx = 0.0;
[10038]1579 _QQ[sys].Row(iPar) = 0.0;
[9258]1580 _QQ[sys].Column(iPar) = 0.0;
[10038]1581 _QQ[sys](iPar, iPar) = pp->sig0 * pp->sig0;
[9258]1582 }
[5585]1583 }
1584 }
1585 }
[5583]1586}
Note: See TracBrowser for help on using the repository browser.