source: ntrip/trunk/BNC/src/PPP_free/bncmodel.cpp@ 6060

Last change on this file since 6060 was 6060, checked in by mervart, 10 years ago
File size: 36.3 KB
Line 
1// Part of BNC, a utility for retrieving decoding and
2// converting GNSS data streams from NTRIP broadcasters.
3//
4// Copyright (C) 2007
5// German Federal Agency for Cartography and Geodesy (BKG)
6// http://www.bkg.bund.de
7// Czech Technical University Prague, Department of Geodesy
8// http://www.fsv.cvut.cz
9//
10// Email: euref-ip@bkg.bund.de
11//
12// This program is free software; you can redistribute it and/or
13// modify it under the terms of the GNU General Public License
14// as published by the Free Software Foundation, version 2.
15//
16// This program is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19// GNU General Public License for more details.
20//
21// You should have received a copy of the GNU General Public License
22// along with this program; if not, write to the Free Software
23// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25/* -------------------------------------------------------------------------
26 * BKG NTRIP Client
27 * -------------------------------------------------------------------------
28 *
29 * Class: bncParam, bncModel
30 *
31 * Purpose: Model for PPP
32 *
33 * Author: L. Mervart
34 *
35 * Created: 01-Dec-2009
36 *
37 * Changes:
38 *
39 * -----------------------------------------------------------------------*/
40
41#include <iomanip>
42#include <cmath>
43#include <newmatio.h>
44#include <sstream>
45
46#include "bncmodel.h"
47#include "bncpppclient.h"
48#include "bnccore.h"
49#include "bncpppclient.h"
50#include "bancroft.h"
51#include "bncutils.h"
52#include "bncantex.h"
53#include "pppOptions.h"
54#include "pppModel.h"
55
56using namespace BNC_PPP;
57using namespace std;
58
59const unsigned MINOBS = 5;
60const double MINELE = 10.0 * M_PI / 180.0;
61const double MAXRES_CODE = 15.0;
62const double MAXRES_PHASE_GPS = 0.04;
63const double MAXRES_PHASE_GLONASS = 0.08;
64const double GLONASS_WEIGHT_FACTOR = 5.0;
65
66// Constructor
67////////////////////////////////////////////////////////////////////////////
68bncParam::bncParam(bncParam::parType typeIn, int indexIn,
69 const QString& prnIn) {
70 type = typeIn;
71 index = indexIn;
72 prn = prnIn;
73 index_old = 0;
74 xx = 0.0;
75 numEpo = 0;
76}
77
78// Destructor
79////////////////////////////////////////////////////////////////////////////
80bncParam::~bncParam() {
81}
82
83// Partial
84////////////////////////////////////////////////////////////////////////////
85double bncParam::partial(t_satData* satData, bool phase) {
86
87 Tracer tracer("bncParam::partial");
88
89 // Coordinates
90 // -----------
91 if (type == CRD_X) {
92 return (xx - satData->xx(1)) / satData->rho;
93 }
94 else if (type == CRD_Y) {
95 return (xx - satData->xx(2)) / satData->rho;
96 }
97 else if (type == CRD_Z) {
98 return (xx - satData->xx(3)) / satData->rho;
99 }
100
101 // Receiver Clocks
102 // ---------------
103 else if (type == RECCLK) {
104 return 1.0;
105 }
106
107 // Troposphere
108 // -----------
109 else if (type == TROPO) {
110 return 1.0 / sin(satData->eleSat);
111 }
112
113 // Glonass Offset
114 // --------------
115 else if (type == GLONASS_OFFSET) {
116 if (satData->prn[0] == 'R') {
117 return 1.0;
118 }
119 else {
120 return 0.0;
121 }
122 }
123
124 // Galileo Offset
125 // --------------
126 else if (type == GALILEO_OFFSET) {
127 if (satData->prn[0] == 'E') {
128 return 1.0;
129 }
130 else {
131 return 0.0;
132 }
133 }
134
135 // Ambiguities
136 // -----------
137 else if (type == AMB_L3) {
138 if (phase && satData->prn == prn) {
139 return 1.0;
140 }
141 else {
142 return 0.0;
143 }
144 }
145
146 // Default return
147 // --------------
148 return 0.0;
149}
150
151// Constructor
152////////////////////////////////////////////////////////////////////////////
153bncModel::bncModel(bncPPPclient* pppClient) {
154
155 _pppClient = pppClient;
156 _staID = pppClient->staID();
157 _opt = pppClient->opt();
158
159 _tides = new t_tides();
160
161 // Antenna Name, ANTEX File
162 // ------------------------
163 _antex = 0;
164 if (!_opt->_antexFileName.empty()) {
165 _antex = new bncAntex(_opt->_antexFileName.c_str());
166 }
167
168 // Bancroft Coordinates
169 // --------------------
170 _xcBanc.ReSize(4); _xcBanc = 0.0;
171 _ellBanc.ReSize(3); _ellBanc = 0.0;
172
173 // Save copy of data (used in outlier detection)
174 // ---------------------------------------------
175 _epoData_sav = new t_epoData();
176}
177
178// Destructor
179////////////////////////////////////////////////////////////////////////////
180bncModel::~bncModel() {
181 delete _tides;
182 for (int ii = 0; ii < _posAverage.size(); ++ii) {
183 delete _posAverage[ii];
184 }
185 delete _antex;
186 for (int iPar = 1; iPar <= _params.size(); iPar++) {
187 delete _params[iPar-1];
188 }
189 for (int iPar = 1; iPar <= _params_sav.size(); iPar++) {
190 delete _params_sav[iPar-1];
191 }
192 delete _epoData_sav;
193}
194
195// Reset Parameters and Variance-Covariance Matrix
196////////////////////////////////////////////////////////////////////////////
197void bncModel::reset() {
198
199 Tracer tracer("bncModel::reset");
200
201 double lastTrp = 0.0;
202 for (int ii = 0; ii < _params.size(); ii++) {
203 bncParam* pp = _params[ii];
204 if (pp->type == bncParam::TROPO) {
205 lastTrp = pp->xx;
206 }
207 delete pp;
208 }
209 _params.clear();
210
211 int nextPar = 0;
212 _params.push_back(new bncParam(bncParam::CRD_X, ++nextPar, ""));
213 _params.push_back(new bncParam(bncParam::CRD_Y, ++nextPar, ""));
214 _params.push_back(new bncParam(bncParam::CRD_Z, ++nextPar, ""));
215 _params.push_back(new bncParam(bncParam::RECCLK, ++nextPar, ""));
216 if (_opt->estTrp()) {
217 _params.push_back(new bncParam(bncParam::TROPO, ++nextPar, ""));
218 }
219 if (_opt->useSystem('R')) {
220 _params.push_back(new bncParam(bncParam::GLONASS_OFFSET, ++nextPar, ""));
221 }
222 if (_opt->useSystem('E')) {
223 _params.push_back(new bncParam(bncParam::GALILEO_OFFSET, ++nextPar, ""));
224 }
225
226 _QQ.ReSize(_params.size());
227 _QQ = 0.0;
228 for (int iPar = 1; iPar <= _params.size(); iPar++) {
229 bncParam* pp = _params[iPar-1];
230 pp->xx = 0.0;
231 if (pp->isCrd()) {
232 _QQ(iPar,iPar) = _opt->_aprSigCrd(1) * _opt->_aprSigCrd(1);
233 }
234 else if (pp->type == bncParam::RECCLK) {
235 _QQ(iPar,iPar) = _opt->_noiseClk * _opt->_noiseClk;
236 }
237 else if (pp->type == bncParam::TROPO) {
238 _QQ(iPar,iPar) = _opt->_aprSigTrp * _opt->_aprSigTrp;
239 pp->xx = lastTrp;
240 }
241 else if (pp->type == bncParam::GLONASS_OFFSET) {
242 _QQ(iPar,iPar) = 1000.0 * 1000.0;
243 }
244 else if (pp->type == bncParam::GALILEO_OFFSET) {
245 _QQ(iPar,iPar) = 1000.0 * 1000.0;
246 }
247 }
248}
249
250// Bancroft Solution
251////////////////////////////////////////////////////////////////////////////
252t_irc bncModel::cmpBancroft(t_epoData* epoData) {
253
254 Tracer tracer("bncModel::cmpBancroft");
255
256 if (epoData->sizeSys('G') < MINOBS) {
257 _log += "bncModel::cmpBancroft: not enough data\n";
258 return failure;
259 }
260
261 Matrix BB(epoData->sizeSys('G'), 4);
262
263 QMapIterator<QString, t_satData*> it(epoData->satData);
264 int iObsBanc = 0;
265 while (it.hasNext()) {
266 it.next();
267 t_satData* satData = it.value();
268 if (satData->system() == 'G') {
269 ++iObsBanc;
270 QString prn = it.key();
271 BB(iObsBanc, 1) = satData->xx(1);
272 BB(iObsBanc, 2) = satData->xx(2);
273 BB(iObsBanc, 3) = satData->xx(3);
274 BB(iObsBanc, 4) = satData->P3 + satData->clk;
275 }
276 }
277
278 bancroft(BB, _xcBanc);
279
280 // Ellipsoidal Coordinates
281 // ------------------------
282 xyz2ell(_xcBanc.data(), _ellBanc.data());
283
284 // Compute Satellite Elevations
285 // ----------------------------
286 QMutableMapIterator<QString, t_satData*> im(epoData->satData);
287 while (im.hasNext()) {
288 im.next();
289 t_satData* satData = im.value();
290 cmpEle(satData);
291 if (satData->eleSat < MINELE) {
292 delete satData;
293 im.remove();
294 }
295 }
296
297 return success;
298}
299
300// Computed Value
301////////////////////////////////////////////////////////////////////////////
302double bncModel::cmpValue(t_satData* satData, bool phase) {
303
304 Tracer tracer("bncModel::cmpValue");
305
306 ColumnVector xRec(3);
307 xRec(1) = x();
308 xRec(2) = y();
309 xRec(3) = z();
310
311 double rho0 = (satData->xx - xRec).norm_Frobenius();
312 double dPhi = t_CST::omega * rho0 / t_CST::c;
313
314 xRec(1) = x() * cos(dPhi) - y() * sin(dPhi);
315 xRec(2) = y() * cos(dPhi) + x() * sin(dPhi);
316 xRec(3) = z();
317
318 xRec += _tides->displacement(_time, xRec);
319
320 satData->rho = (satData->xx - xRec).norm_Frobenius();
321
322 double tropDelay = delay_saast(satData->eleSat) +
323 trp() / sin(satData->eleSat);
324
325 double wind = 0.0;
326 if (phase) {
327 wind = windUp(satData->prn, satData->xx, xRec) * satData->lambda3;
328 }
329
330 double offset = 0.0;
331 if (satData->prn[0] == 'R') {
332 offset = Glonass_offset();
333 }
334 else if (satData->prn[0] == 'E') {
335 offset = Galileo_offset();
336 }
337
338 double phaseCenter = 0.0;
339 if (_antex) {
340 bool found;
341 phaseCenter = _antex->pco(_opt->antennaName, satData->eleSat, found);
342 if (!found) {
343 _pppClient->emitNewMessage("ANTEX: antenna >"
344 + _opt->antennaName.toAscii() + "< not found", true);
345 }
346 }
347
348 double antennaOffset = 0.0;
349 if (_opt->antEccSet()) {
350 double cosa = cos(satData->azSat);
351 double sina = sin(satData->azSat);
352 double cose = cos(satData->eleSat);
353 double sine = sin(satData->eleSat);
354 antennaOffset = -_opt->antEccNEU[0] * cosa*cose
355 -_opt->antEccNEU[1] * sina*cose
356 -_opt->antEccNEU[2] * sine;
357 }
358
359 return satData->rho + phaseCenter + antennaOffset + clk()
360 + offset - satData->clk + tropDelay + wind;
361}
362
363// Tropospheric Model (Saastamoinen)
364////////////////////////////////////////////////////////////////////////////
365double bncModel::delay_saast(double Ele) {
366
367 Tracer tracer("bncModel::delay_saast");
368
369 double xyz[3];
370 xyz[0] = x();
371 xyz[1] = y();
372 xyz[2] = z();
373 double ell[3];
374 xyz2ell(xyz, ell);
375 double height = ell[2];
376
377 double pp = 1013.25 * pow(1.0 - 2.26e-5 * height, 5.225);
378 double TT = 18.0 - height * 0.0065 + 273.15;
379 double hh = 50.0 * exp(-6.396e-4 * height);
380 double ee = hh / 100.0 * exp(-37.2465 + 0.213166*TT - 0.000256908*TT*TT);
381
382 double h_km = height / 1000.0;
383
384 if (h_km < 0.0) h_km = 0.0;
385 if (h_km > 5.0) h_km = 5.0;
386 int ii = int(h_km + 1);
387 double href = ii - 1;
388
389 double bCor[6];
390 bCor[0] = 1.156;
391 bCor[1] = 1.006;
392 bCor[2] = 0.874;
393 bCor[3] = 0.757;
394 bCor[4] = 0.654;
395 bCor[5] = 0.563;
396
397 double BB = bCor[ii-1] + (bCor[ii]-bCor[ii-1]) * (h_km - href);
398
399 double zen = M_PI/2.0 - Ele;
400
401 return (0.002277/cos(zen)) * (pp + ((1255.0/TT)+0.05)*ee - BB*(tan(zen)*tan(zen)));
402}
403
404// Prediction Step of the Filter
405////////////////////////////////////////////////////////////////////////////
406void bncModel::predict(int iPhase, t_epoData* epoData) {
407
408 Tracer tracer("bncModel::predict");
409
410 if (iPhase == 0) {
411
412 bool firstCrd = false;
413 if (!_lastTimeOK.valid() || (_opt->maxSolGap > 0 && _time - _lastTimeOK > _opt->maxSolGap)) {
414 firstCrd = true;
415 _startTime = epoData->tt;
416 reset();
417 }
418
419 // Use different white noise for Quick-Start mode
420 // ----------------------------------------------
421 double sigCrdP_used = _opt->sigCrdP;
422 if ( _opt->quickStart > 0.0 && _opt->quickStart > (epoData->tt - _startTime) ) {
423 sigCrdP_used = 0.0;
424 }
425
426 // Predict Parameter values, add white noise
427 // -----------------------------------------
428 for (int iPar = 1; iPar <= _params.size(); iPar++) {
429 bncParam* pp = _params[iPar-1];
430
431 // Coordinates
432 // -----------
433 if (pp->type == bncParam::CRD_X) {
434 if (firstCrd) {
435 if (_opt->refCrdSet()) {
436 pp->xx = _opt->refCrd[0];
437 }
438 else {
439 pp->xx = _xcBanc(1);
440 }
441 }
442 _QQ(iPar,iPar) += sigCrdP_used * sigCrdP_used;
443 }
444 else if (pp->type == bncParam::CRD_Y) {
445 if (firstCrd) {
446 if (_opt->refCrdSet()) {
447 pp->xx = _opt->refCrd[1];
448 }
449 else {
450 pp->xx = _xcBanc(2);
451 }
452 }
453 _QQ(iPar,iPar) += sigCrdP_used * sigCrdP_used;
454 }
455 else if (pp->type == bncParam::CRD_Z) {
456 if (firstCrd) {
457 if (_opt->refCrdSet()) {
458 pp->xx = _opt->refCrd[2];
459 }
460 else {
461 pp->xx = _xcBanc(3);
462 }
463 }
464 _QQ(iPar,iPar) += sigCrdP_used * sigCrdP_used;
465 }
466
467 // Receiver Clocks
468 // ---------------
469 else if (pp->type == bncParam::RECCLK) {
470 pp->xx = _xcBanc(4);
471 for (int jj = 1; jj <= _params.size(); jj++) {
472 _QQ(iPar, jj) = 0.0;
473 }
474 _QQ(iPar,iPar) = _opt->sigClk0 * _opt->sigClk0;
475 }
476
477 // Tropospheric Delay
478 // ------------------
479 else if (pp->type == bncParam::TROPO) {
480 _QQ(iPar,iPar) += _opt->sigTrpP * _opt->sigTrpP;
481 }
482
483 // Glonass Offset
484 // --------------
485 else if (pp->type == bncParam::GLONASS_OFFSET) {
486 bool epoSpec = true;
487 if (epoSpec) {
488 pp->xx = 0.0;
489 for (int jj = 1; jj <= _params.size(); jj++) {
490 _QQ(iPar, jj) = 0.0;
491 }
492 _QQ(iPar,iPar) = _opt->sigGlonassOffset0 * _opt->sigGlonassOffset0;
493 }
494 else {
495 _QQ(iPar,iPar) += _opt->sigGlonassOffsetP * _opt->sigGlonassOffsetP;
496 }
497 }
498
499 // Galileo Offset
500 // --------------
501 else if (pp->type == bncParam::GALILEO_OFFSET) {
502 _QQ(iPar,iPar) += _opt->sigGalileoOffsetP * _opt->sigGalileoOffsetP;
503 }
504 }
505 }
506
507 // Add New Ambiguities if necessary
508 // --------------------------------
509 if (_opt->usePhase) {
510
511 // Make a copy of QQ and xx, set parameter indices
512 // -----------------------------------------------
513 SymmetricMatrix QQ_old = _QQ;
514
515 for (int iPar = 1; iPar <= _params.size(); iPar++) {
516 _params[iPar-1]->index_old = _params[iPar-1]->index;
517 _params[iPar-1]->index = 0;
518 }
519
520 // Remove Ambiguity Parameters without observations
521 // ------------------------------------------------
522 int iPar = 0;
523 QMutableVectorIterator<bncParam*> im(_params);
524 while (im.hasNext()) {
525 bncParam* par = im.next();
526 bool removed = false;
527 if (par->type == bncParam::AMB_L3) {
528 if (epoData->satData.find(par->prn) == epoData->satData.end()) {
529 removed = true;
530 delete par;
531 im.remove();
532 }
533 }
534 if (! removed) {
535 ++iPar;
536 par->index = iPar;
537 }
538 }
539
540 // Add new ambiguity parameters
541 // ----------------------------
542 QMapIterator<QString, t_satData*> it(epoData->satData);
543 while (it.hasNext()) {
544 it.next();
545 t_satData* satData = it.value();
546 addAmb(satData);
547 }
548
549 int nPar = _params.size();
550 _QQ.ReSize(nPar); _QQ = 0.0;
551 for (int i1 = 1; i1 <= nPar; i1++) {
552 bncParam* p1 = _params[i1-1];
553 if (p1->index_old != 0) {
554 _QQ(p1->index, p1->index) = QQ_old(p1->index_old, p1->index_old);
555 for (int i2 = 1; i2 <= nPar; i2++) {
556 bncParam* p2 = _params[i2-1];
557 if (p2->index_old != 0) {
558 _QQ(p1->index, p2->index) = QQ_old(p1->index_old, p2->index_old);
559 }
560 }
561 }
562 }
563
564 for (int ii = 1; ii <= nPar; ii++) {
565 bncParam* par = _params[ii-1];
566 if (par->index_old == 0) {
567 _QQ(par->index, par->index) = _opt->sigAmb0 * _opt->sigAmb0;
568 }
569 par->index_old = par->index;
570 }
571 }
572}
573
574// Update Step of the Filter (currently just a single-epoch solution)
575////////////////////////////////////////////////////////////////////////////
576t_irc bncModel::update(t_epoData* epoData) {
577
578 Tracer tracer("bncModel::update");
579
580 _log.clear();
581
582 _time = epoData->tt; // current epoch time
583
584 if (_opt->pppMode) {
585 _log += "Precise Point Positioning of Epoch "
586 + QByteArray(_time.timestr(1).c_str()) +
587 "\n---------------------------------------------------------------\n";
588 }
589 else {
590 _log += "Single Point Positioning of Epoch "
591 + QByteArray(_time.timestr(1).c_str()) +
592 "\n--------------------------------------------------------------\n";
593 }
594
595 // Outlier Detection Loop
596 // ----------------------
597 if (update_p(epoData) != success) {
598 _pppClient->emitNewMessage(_log, false);
599 return failure;
600 }
601
602 // Remember the Epoch-specific Results for the computation of means
603 // ----------------------------------------------------------------
604 pppPos* newPos = new pppPos;
605 newPos->time = epoData->tt;
606
607 // Set Solution Vector
608 // -------------------
609 ostringstream strB;
610 strB.setf(ios::fixed);
611 QVectorIterator<bncParam*> itPar(_params);
612 while (itPar.hasNext()) {
613 bncParam* par = itPar.next();
614
615 if (par->type == bncParam::RECCLK) {
616 strB << "\n clk = " << setw(10) << setprecision(3) << par->xx
617 << " +- " << setw(6) << setprecision(3)
618 << sqrt(_QQ(par->index,par->index));
619 }
620 else if (par->type == bncParam::AMB_L3) {
621 ++par->numEpo;
622 strB << "\n amb " << par->prn.toAscii().data() << " = "
623 << setw(10) << setprecision(3) << par->xx
624 << " +- " << setw(6) << setprecision(3)
625 << sqrt(_QQ(par->index,par->index))
626 << " nEpo = " << par->numEpo;
627 }
628 else if (par->type == bncParam::TROPO) {
629 double aprTrp = delay_saast(M_PI/2.0);
630 strB << "\n trp = " << par->prn.toAscii().data()
631 << setw(7) << setprecision(3) << aprTrp << " "
632 << setw(6) << setprecision(3) << showpos << par->xx << noshowpos
633 << " +- " << setw(6) << setprecision(3)
634 << sqrt(_QQ(par->index,par->index));
635 newPos->xnt[6] = aprTrp + par->xx;
636 }
637 else if (par->type == bncParam::GLONASS_OFFSET) {
638 strB << "\n offGlo = " << setw(10) << setprecision(3) << par->xx
639 << " +- " << setw(6) << setprecision(3)
640 << sqrt(_QQ(par->index,par->index));
641 }
642 else if (par->type == bncParam::GALILEO_OFFSET) {
643 strB << "\n offGal = " << setw(10) << setprecision(3) << par->xx
644 << " +- " << setw(6) << setprecision(3)
645 << sqrt(_QQ(par->index,par->index));
646 }
647 }
648 strB << '\n';
649 _log += strB.str().c_str();
650 _pppClient->emitNewMessage(_log, false);
651
652 // Final Message (both log file and screen)
653 // ----------------------------------------
654 ostringstream strC;
655 strC.setf(ios::fixed);
656 strC << _staID.data() << " PPP "
657 << epoData->tt.timestr(1) << " " << epoData->sizeAll() << " "
658 << setw(14) << setprecision(3) << x() << " +- "
659 << setw(6) << setprecision(3) << sqrt(_QQ(1,1)) << " "
660 << setw(14) << setprecision(3) << y() << " +- "
661 << setw(6) << setprecision(3) << sqrt(_QQ(2,2)) << " "
662 << setw(14) << setprecision(3) << z() << " +- "
663 << setw(6) << setprecision(3) << sqrt(_QQ(3,3));
664
665 // NEU Output
666 // ----------
667 if (_opt->refCrdSet()) {
668 newPos->xnt[0] = x() - _opt->refCrd[0];
669 newPos->xnt[1] = y() - _opt->refCrd[1];
670 newPos->xnt[2] = z() - _opt->refCrd[2];
671
672 double ellRef[3];
673 xyz2ell(_opt->refCrd, ellRef);
674 xyz2neu(ellRef, newPos->xnt, &newPos->xnt[3]);
675
676 strC << " NEU "
677 << setw(8) << setprecision(3) << newPos->xnt[3] << " "
678 << setw(8) << setprecision(3) << newPos->xnt[4] << " "
679 << setw(8) << setprecision(3) << newPos->xnt[5] << endl;
680
681 }
682
683 _pppClient->emitNewMessage(QByteArray(strC.str().c_str()), true);
684
685 if (_opt->pppAverage == 0.0) {
686 delete newPos;
687 }
688 else {
689
690 _posAverage.push_back(newPos);
691
692 // Compute the Mean
693 // ----------------
694 ColumnVector mean(7); mean = 0.0;
695
696 QMutableVectorIterator<pppPos*> it(_posAverage);
697 while (it.hasNext()) {
698 pppPos* pp = it.next();
699 if ( (epoData->tt - pp->time) >= _opt->pppAverage ) {
700 delete pp;
701 it.remove();
702 }
703 else {
704 for (int ii = 0; ii < 7; ++ii) {
705 mean[ii] += pp->xnt[ii];
706 }
707 }
708 }
709
710 int nn = _posAverage.size();
711
712 if (nn > 0) {
713
714 mean /= nn;
715
716 // Compute the Deviation
717 // ---------------------
718 ColumnVector std(7); std = 0.0;
719 QVectorIterator<pppPos*> it2(_posAverage);
720 while (it2.hasNext()) {
721 pppPos* pp = it2.next();
722 for (int ii = 0; ii < 7; ++ii) {
723 std[ii] += (pp->xnt[ii] - mean[ii]) * (pp->xnt[ii] - mean[ii]);
724 }
725 }
726 for (int ii = 0; ii < 7; ++ii) {
727 std[ii] = sqrt(std[ii] / nn);
728 }
729
730 if (_opt->refCrdSet()) {
731 ostringstream strD; strD.setf(ios::fixed);
732 strD << _staID.data() << " AVE-XYZ "
733 << epoData->tt.timestr(1) << " "
734 << setw(13) << setprecision(3) << mean[0] + _opt->refCrd[0] << " +- "
735 << setw(6) << setprecision(3) << std[0] << " "
736 << setw(14) << setprecision(3) << mean[1] + _opt->refCrd[1] << " +- "
737 << setw(6) << setprecision(3) << std[1] << " "
738 << setw(14) << setprecision(3) << mean[2] + _opt->refCrd[2] << " +- "
739 << setw(6) << setprecision(3) << std[2];
740 _pppClient->emitNewMessage(QByteArray(strD.str().c_str()), true);
741
742 ostringstream strE; strE.setf(ios::fixed);
743 strE << _staID.data() << " AVE-NEU "
744 << epoData->tt.timestr(1) << " "
745 << setw(13) << setprecision(3) << mean[3] << " +- "
746 << setw(6) << setprecision(3) << std[3] << " "
747 << setw(14) << setprecision(3) << mean[4] << " +- "
748 << setw(6) << setprecision(3) << std[4] << " "
749 << setw(14) << setprecision(3) << mean[5] << " +- "
750 << setw(6) << setprecision(3) << std[5];
751 _pppClient->emitNewMessage(QByteArray(strE.str().c_str()), true);
752
753 if (_opt->estTropo) {
754 ostringstream strF; strF.setf(ios::fixed);
755 strF << _staID.data() << " AVE-TRP "
756 << epoData->tt.timestr(1) << " "
757 << setw(13) << setprecision(3) << mean[6] << " +- "
758 << setw(6) << setprecision(3) << std[6] << endl;
759 _pppClient->emitNewMessage(QByteArray(strF.str().c_str()), true);
760 }
761 }
762 }
763 }
764
765 _lastTimeOK = _time; // remember time of last successful update
766 return success;
767}
768
769// Outlier Detection
770////////////////////////////////////////////////////////////////////////////
771QString bncModel::outlierDetection(int iPhase, const ColumnVector& vv,
772 QMap<QString, t_satData*>& satData) {
773
774 Tracer tracer("bncModel::outlierDetection");
775
776 QString prnGPS;
777 QString prnGlo;
778 double maxResGPS = 0.0;
779 double maxResGlo = 0.0;
780 findMaxRes(vv, satData, prnGPS, prnGlo, maxResGPS, maxResGlo);
781
782 if (iPhase == 1) {
783 if (maxResGlo > MAXRES_PHASE_GLONASS) {
784 _log += "Outlier Phase " + prnGlo + " "
785 + QByteArray::number(maxResGlo, 'f', 3) + "\n";
786 return prnGlo;
787 }
788 else if (maxResGPS > MAXRES_PHASE_GPS) {
789 _log += "Outlier Phase " + prnGPS + " "
790 + QByteArray::number(maxResGPS, 'f', 3) + "\n";
791 return prnGPS;
792 }
793 }
794 else if (iPhase == 0 && maxResGPS > MAXRES_CODE) {
795 _log += "Outlier Code " + prnGPS + " "
796 + QByteArray::number(maxResGPS, 'f', 3) + "\n";
797 return prnGPS;
798 }
799
800 return QString();
801}
802
803//
804//////////////////////////////////////////////////////////////////////////////
805void bncModel::kalman(const Matrix& AA, const ColumnVector& ll,
806 const DiagonalMatrix& PP,
807 SymmetricMatrix& QQ, ColumnVector& dx) {
808
809 Tracer tracer("bncModel::kalman");
810
811 int nPar = AA.Ncols();
812 int nObs = AA.Nrows();
813 UpperTriangularMatrix SS = Cholesky(QQ).t();
814
815 Matrix SA = SS*AA.t();
816 Matrix SRF(nObs+nPar, nObs+nPar); SRF = 0;
817 for (int ii = 1; ii <= nObs; ++ii) {
818 SRF(ii,ii) = 1.0 / sqrt(PP(ii,ii));
819 }
820
821 SRF.SubMatrix (nObs+1, nObs+nPar, 1, nObs) = SA;
822 SRF.SymSubMatrix(nObs+1, nObs+nPar) = SS;
823
824 UpperTriangularMatrix UU;
825 QRZ(SRF, UU);
826
827 SS = UU.SymSubMatrix(nObs+1, nObs+nPar);
828 UpperTriangularMatrix SH_rt = UU.SymSubMatrix(1, nObs);
829 Matrix YY = UU.SubMatrix(1, nObs, nObs+1, nObs+nPar);
830
831 UpperTriangularMatrix SHi = SH_rt.i();
832
833 Matrix KT = SHi * YY;
834 SymmetricMatrix Hi; Hi << SHi * SHi.t();
835
836 dx = KT.t() * ll;
837 QQ << (SS.t() * SS);
838}
839
840// Phase Wind-Up Correction
841///////////////////////////////////////////////////////////////////////////
842double bncModel::windUp(const QString& prn, const ColumnVector& rSat,
843 const ColumnVector& rRec) {
844
845 Tracer tracer("bncModel::windUp");
846
847 double Mjd = _time.mjd() + _time.daysec() / 86400.0;
848
849 // First time - initialize to zero
850 // -------------------------------
851 if (!_windUpTime.contains(prn)) {
852 _windUpSum[prn] = 0.0;
853 }
854
855 // Compute the correction for new time
856 // -----------------------------------
857 if (!_windUpTime.contains(prn) || _windUpTime[prn] != Mjd) {
858 _windUpTime[prn] = Mjd;
859
860 // Unit Vector GPS Satellite --> Receiver
861 // --------------------------------------
862 ColumnVector rho = rRec - rSat;
863 rho /= rho.norm_Frobenius();
864
865 // GPS Satellite unit Vectors sz, sy, sx
866 // -------------------------------------
867 ColumnVector sz = -rSat / rSat.norm_Frobenius();
868
869 ColumnVector xSun = Sun(Mjd);
870 xSun /= xSun.norm_Frobenius();
871
872 ColumnVector sy = crossproduct(sz, xSun);
873 ColumnVector sx = crossproduct(sy, sz);
874
875 // Effective Dipole of the GPS Satellite Antenna
876 // ---------------------------------------------
877 ColumnVector dipSat = sx - rho * DotProduct(rho,sx)
878 - crossproduct(rho, sy);
879
880 // Receiver unit Vectors rx, ry
881 // ----------------------------
882 ColumnVector rx(3);
883 ColumnVector ry(3);
884
885 double recEll[3]; xyz2ell(rRec.data(), recEll) ;
886 double neu[3];
887
888 neu[0] = 1.0;
889 neu[1] = 0.0;
890 neu[2] = 0.0;
891 neu2xyz(recEll, neu, rx.data());
892
893 neu[0] = 0.0;
894 neu[1] = -1.0;
895 neu[2] = 0.0;
896 neu2xyz(recEll, neu, ry.data());
897
898 // Effective Dipole of the Receiver Antenna
899 // ----------------------------------------
900 ColumnVector dipRec = rx - rho * DotProduct(rho,rx)
901 + crossproduct(rho, ry);
902
903 // Resulting Effect
904 // ----------------
905 double alpha = DotProduct(dipSat,dipRec) /
906 (dipSat.norm_Frobenius() * dipRec.norm_Frobenius());
907
908 if (alpha > 1.0) alpha = 1.0;
909 if (alpha < -1.0) alpha = -1.0;
910
911 double dphi = acos(alpha) / 2.0 / M_PI; // in cycles
912
913 if ( DotProduct(rho, crossproduct(dipSat, dipRec)) < 0.0 ) {
914 dphi = -dphi;
915 }
916
917 _windUpSum[prn] = floor(_windUpSum[prn] - dphi + 0.5) + dphi;
918 }
919
920 return _windUpSum[prn];
921}
922
923//
924///////////////////////////////////////////////////////////////////////////
925void bncModel::cmpEle(t_satData* satData) {
926 Tracer tracer("bncModel::cmpEle");
927 ColumnVector rr = satData->xx - _xcBanc.Rows(1,3);
928 double rho = rr.norm_Frobenius();
929
930 double neu[3];
931 xyz2neu(_ellBanc.data(), rr.data(), neu);
932
933 satData->eleSat = acos( sqrt(neu[0]*neu[0] + neu[1]*neu[1]) / rho );
934 if (neu[2] < 0) {
935 satData->eleSat *= -1.0;
936 }
937 satData->azSat = atan2(neu[1], neu[0]);
938}
939
940//
941///////////////////////////////////////////////////////////////////////////
942void bncModel::addAmb(t_satData* satData) {
943 Tracer tracer("bncModel::addAmb");
944 bool found = false;
945 for (int iPar = 1; iPar <= _params.size(); iPar++) {
946 if (_params[iPar-1]->type == bncParam::AMB_L3 &&
947 _params[iPar-1]->prn == satData->prn) {
948 found = true;
949 break;
950 }
951 }
952 if (!found) {
953 bncParam* par = new bncParam(bncParam::AMB_L3,
954 _params.size()+1, satData->prn);
955 _params.push_back(par);
956 par->xx = satData->L3 - cmpValue(satData, true);
957 }
958}
959
960//
961///////////////////////////////////////////////////////////////////////////
962void bncModel::addObs(int iPhase, unsigned& iObs, t_satData* satData,
963 Matrix& AA, ColumnVector& ll, DiagonalMatrix& PP) {
964
965 Tracer tracer("bncModel::addObs");
966
967 const double ELEWGHT = 20.0;
968 double ellWgtCoef = 1.0;
969 double eleD = satData->eleSat * 180.0 / M_PI;
970 if (eleD < ELEWGHT) {
971 ellWgtCoef = 1.5 - 0.5 / (ELEWGHT - 10.0) * (eleD - 10.0);
972 }
973
974 // Remember Observation Index
975 // --------------------------
976 ++iObs;
977 satData->obsIndex = iObs;
978
979 // Phase Observations
980 // ------------------
981 if (iPhase == 1) {
982 ll(iObs) = satData->L3 - cmpValue(satData, true);
983 double sigL3 = _opt->sigL3;
984 if (satData->system() == 'R') {
985 sigL3 *= GLONASS_WEIGHT_FACTOR;
986 }
987 PP(iObs,iObs) = 1.0 / (sigL3 * sigL3) / (ellWgtCoef * ellWgtCoef);
988 for (int iPar = 1; iPar <= _params.size(); iPar++) {
989 if (_params[iPar-1]->type == bncParam::AMB_L3 &&
990 _params[iPar-1]->prn == satData->prn) {
991 ll(iObs) -= _params[iPar-1]->xx;
992 }
993 AA(iObs, iPar) = _params[iPar-1]->partial(satData, true);
994 }
995 }
996
997 // Code Observations
998 // -----------------
999 else {
1000 ll(iObs) = satData->P3 - cmpValue(satData, false);
1001 PP(iObs,iObs) = 1.0 / (_opt->sigP3 * _opt->sigP3) / (ellWgtCoef * ellWgtCoef);
1002 for (int iPar = 1; iPar <= _params.size(); iPar++) {
1003 AA(iObs, iPar) = _params[iPar-1]->partial(satData, false);
1004 }
1005 }
1006}
1007
1008//
1009///////////////////////////////////////////////////////////////////////////
1010QByteArray bncModel::printRes(int iPhase, const ColumnVector& vv,
1011 const QMap<QString, t_satData*>& satDataMap) {
1012
1013 Tracer tracer("bncModel::printRes");
1014
1015 ostringstream str;
1016 str.setf(ios::fixed);
1017
1018 QMapIterator<QString, t_satData*> it(satDataMap);
1019 while (it.hasNext()) {
1020 it.next();
1021 t_satData* satData = it.value();
1022 if (satData->obsIndex != 0) {
1023 str << _time.timestr(1)
1024 << " RES " << satData->prn.toAscii().data()
1025 << (iPhase ? " L3 " : " P3 ")
1026 << setw(9) << setprecision(4) << vv(satData->obsIndex) << endl;
1027 }
1028 }
1029
1030 return QByteArray(str.str().c_str());
1031}
1032
1033//
1034///////////////////////////////////////////////////////////////////////////
1035void bncModel::findMaxRes(const ColumnVector& vv,
1036 const QMap<QString, t_satData*>& satData,
1037 QString& prnGPS, QString& prnGlo,
1038 double& maxResGPS, double& maxResGlo) {
1039
1040 Tracer tracer("bncModel::findMaxRes");
1041
1042 maxResGPS = 0.0;
1043 maxResGlo = 0.0;
1044
1045 QMapIterator<QString, t_satData*> it(satData);
1046 while (it.hasNext()) {
1047 it.next();
1048 t_satData* satData = it.value();
1049 if (satData->obsIndex != 0) {
1050 QString prn = satData->prn;
1051 if (prn[0] == 'R') {
1052 if (fabs(vv(satData->obsIndex)) > maxResGlo) {
1053 maxResGlo = fabs(vv(satData->obsIndex));
1054 prnGlo = prn;
1055 }
1056 }
1057 else {
1058 if (fabs(vv(satData->obsIndex)) > maxResGPS) {
1059 maxResGPS = fabs(vv(satData->obsIndex));
1060 prnGPS = prn;
1061 }
1062 }
1063 }
1064 }
1065}
1066
1067// Update Step (private - loop over outliers)
1068////////////////////////////////////////////////////////////////////////////
1069t_irc bncModel::update_p(t_epoData* epoData) {
1070
1071 Tracer tracer("bncModel::update_p");
1072
1073 // Save Variance-Covariance Matrix, and Status Vector
1074 // --------------------------------------------------
1075 rememberState(epoData);
1076
1077 QString lastOutlierPrn;
1078
1079 // Try with all satellites, then with all minus one, etc.
1080 // ------------------------------------------------------
1081 while (selectSatellites(lastOutlierPrn, epoData->satData) == success) {
1082
1083 QByteArray strResCode;
1084 QByteArray strResPhase;
1085
1086 // Bancroft Solution
1087 // -----------------
1088 if (cmpBancroft(epoData) != success) {
1089 break;
1090 }
1091
1092 // First update using code observations, then phase observations
1093 // -------------------------------------------------------------
1094 for (int iPhase = 0; iPhase <= (_opt->usePhase ? 1 : 0); iPhase++) {
1095
1096 // Status Prediction
1097 // -----------------
1098 predict(iPhase, epoData);
1099
1100 // Create First-Design Matrix
1101 // --------------------------
1102 unsigned nPar = _params.size();
1103 unsigned nObs = 0;
1104 if (iPhase == 0) {
1105 nObs = epoData->sizeAll() - epoData->sizeSys('R'); // Glonass code not used
1106 }
1107 else {
1108 nObs = epoData->sizeAll();
1109 }
1110
1111 // Prepare first-design Matrix, vector observed-computed
1112 // -----------------------------------------------------
1113 Matrix AA(nObs, nPar); // first design matrix
1114 ColumnVector ll(nObs); // tems observed-computed
1115 DiagonalMatrix PP(nObs); PP = 0.0;
1116
1117 unsigned iObs = 0;
1118 QMapIterator<QString, t_satData*> it(epoData->satData);
1119 while (it.hasNext()) {
1120 it.next();
1121 t_satData* satData = it.value();
1122 if (iPhase == 1 || satData->system() != 'R') {
1123 QString prn = satData->prn;
1124 addObs(iPhase, iObs, satData, AA, ll, PP);
1125 }
1126 }
1127
1128 // Compute Filter Update
1129 // ---------------------
1130 ColumnVector dx;
1131 kalman(AA, ll, PP, _QQ, dx);
1132 ColumnVector vv = ll - AA * dx;
1133
1134 // Print Residuals
1135 // ---------------
1136 if (iPhase == 0) {
1137 strResCode = printRes(iPhase, vv, epoData->satData);
1138 }
1139 else {
1140 strResPhase = printRes(iPhase, vv, epoData->satData);
1141 }
1142
1143 // Check the residuals
1144 // -------------------
1145 lastOutlierPrn = outlierDetection(iPhase, vv, epoData->satData);
1146
1147 // No Outlier Detected
1148 // -------------------
1149 if (lastOutlierPrn.isEmpty()) {
1150
1151 QVectorIterator<bncParam*> itPar(_params);
1152 while (itPar.hasNext()) {
1153 bncParam* par = itPar.next();
1154 par->xx += dx(par->index);
1155 }
1156
1157 if (!_opt->usePhase || iPhase == 1) {
1158 if (_outlierGPS.size() > 0 || _outlierGlo.size() > 0) {
1159 _log += "Neglected PRNs: ";
1160 if (!_outlierGPS.isEmpty()) {
1161 _log += _outlierGPS.last() + ' ';
1162 }
1163 QStringListIterator itGlo(_outlierGlo);
1164 while (itGlo.hasNext()) {
1165 QString prn = itGlo.next();
1166 _log += prn + ' ';
1167 }
1168 }
1169 _log += '\n';
1170
1171 _log += strResCode + strResPhase;
1172
1173 return success;
1174 }
1175 }
1176
1177 // Outlier Found
1178 // -------------
1179 else {
1180 restoreState(epoData);
1181 break;
1182 }
1183
1184 } // for iPhase
1185
1186 } // while selectSatellites
1187
1188 restoreState(epoData);
1189 return failure;
1190}
1191
1192// Remeber Original State Vector and Variance-Covariance Matrix
1193////////////////////////////////////////////////////////////////////////////
1194void bncModel::rememberState(t_epoData* epoData) {
1195
1196 _QQ_sav = _QQ;
1197
1198 QVectorIterator<bncParam*> itSav(_params_sav);
1199 while (itSav.hasNext()) {
1200 bncParam* par = itSav.next();
1201 delete par;
1202 }
1203 _params_sav.clear();
1204
1205 QVectorIterator<bncParam*> it(_params);
1206 while (it.hasNext()) {
1207 bncParam* par = it.next();
1208 _params_sav.push_back(new bncParam(*par));
1209 }
1210
1211 _epoData_sav->deepCopy(epoData);
1212}
1213
1214// Restore Original State Vector and Variance-Covariance Matrix
1215////////////////////////////////////////////////////////////////////////////
1216void bncModel::restoreState(t_epoData* epoData) {
1217
1218 _QQ = _QQ_sav;
1219
1220 QVectorIterator<bncParam*> it(_params);
1221 while (it.hasNext()) {
1222 bncParam* par = it.next();
1223 delete par;
1224 }
1225 _params.clear();
1226
1227 QVectorIterator<bncParam*> itSav(_params_sav);
1228 while (itSav.hasNext()) {
1229 bncParam* par = itSav.next();
1230 _params.push_back(new bncParam(*par));
1231 }
1232
1233 epoData->deepCopy(_epoData_sav);
1234}
1235
1236//
1237////////////////////////////////////////////////////////////////////////////
1238t_irc bncModel::selectSatellites(const QString& lastOutlierPrn,
1239 QMap<QString, t_satData*>& satData) {
1240
1241 // First Call
1242 // ----------
1243 if (lastOutlierPrn.isEmpty()) {
1244 _outlierGPS.clear();
1245 _outlierGlo.clear();
1246 return success;
1247 }
1248
1249 // Second and next trials
1250 // ----------------------
1251 else {
1252
1253 if (lastOutlierPrn[0] == 'R') {
1254 _outlierGlo << lastOutlierPrn;
1255 }
1256
1257 // Remove all Glonass Outliers
1258 // ---------------------------
1259 QStringListIterator it(_outlierGlo);
1260 while (it.hasNext()) {
1261 QString prn = it.next();
1262 if (satData.contains(prn)) {
1263 delete satData.take(prn);
1264 }
1265 }
1266
1267 if (lastOutlierPrn[0] == 'R') {
1268 _outlierGPS.clear();
1269 return success;
1270 }
1271
1272 // GPS Outlier appeared for the first time - try to delete it
1273 // ----------------------------------------------------------
1274 if (_outlierGPS.indexOf(lastOutlierPrn) == -1) {
1275 _outlierGPS << lastOutlierPrn;
1276 if (satData.contains(lastOutlierPrn)) {
1277 delete satData.take(lastOutlierPrn);
1278 }
1279 return success;
1280 }
1281
1282 }
1283
1284 return failure;
1285}
Note: See TracBrowser for help on using the repository browser.