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

Last change on this file since 10607 was 10599, checked in by stuerze, 3 weeks ago

ADDED: consideration of NAV type in all applications

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