source: ntrip/trunk/BNC/bncmodel.cpp@ 2247

Last change on this file since 2247 was 2247, checked in by mervart, 14 years ago

* empty log message *

File size: 25.1 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 "bncapp.h"
48#include "bncpppclient.h"
49#include "bancroft.h"
50#include "bncutils.h"
51#include "bncsettings.h"
52
53using namespace std;
54
55const unsigned MINOBS = 4;
56const double MINELE = 10.0 * M_PI / 180.0;
57const double MAXRES_CODE_GPS = 10.0;
58const double MAXRES_PHASE_GPS = 0.10;
59const double MAXRES_PHASE_GLO = 0.50;
60const double sig_crd_0 = 100.0;
61const double sig_crd_p = 100.0;
62const double sig_clk_0 = 1000.0;
63const double sig_trp_0 = 0.01;
64const double sig_trp_p = 1e-6;
65const double sig_amb_0 = 1000.0;
66const double sig_P3 = 1.0;
67const double sig_L3_GPS = 0.01;
68const double sig_L3_GLO = 0.10;
69
70// Constructor
71////////////////////////////////////////////////////////////////////////////
72bncParam::bncParam(bncParam::parType typeIn, int indexIn,
73 const QString& prnIn) {
74 type = typeIn;
75 index = indexIn;
76 prn = prnIn;
77 index_old = 0;
78 xx = 0.0;
79}
80
81// Destructor
82////////////////////////////////////////////////////////////////////////////
83bncParam::~bncParam() {
84}
85
86// Partial
87////////////////////////////////////////////////////////////////////////////
88double bncParam::partial(t_satData* satData, bool phase) {
89
90 // Coordinates
91 // -----------
92 if (type == CRD_X) {
93 return (xx - satData->xx(1)) / satData->rho;
94 }
95 else if (type == CRD_Y) {
96 return (xx - satData->xx(2)) / satData->rho;
97 }
98 else if (type == CRD_Z) {
99 return (xx - satData->xx(3)) / satData->rho;
100 }
101
102 // Receiver Clocks
103 // ---------------
104 else if (type == RECCLK_GPS) {
105 if (satData->prn[0] == 'G') {
106 return 1.0;
107 }
108 else {
109 return 0.0;
110 }
111 }
112 else if (type == RECCLK_GLO) {
113 if (satData->prn[0] == 'R') {
114 return 1.0;
115 }
116 else {
117 return 0.0;
118 }
119 }
120
121 // Troposphere
122 // -----------
123 else if (type == TROPO) {
124 return 1.0 / sin(satData->eleSat);
125 }
126
127 // Ambiguities
128 // -----------
129 else if (type == AMB_L3) {
130 if (phase && satData->prn == prn) {
131 return 1.0;
132 }
133 else {
134 return 0.0;
135 }
136 }
137
138 // Default return
139 // --------------
140 return 0.0;
141}
142
143// Constructor
144////////////////////////////////////////////////////////////////////////////
145bncModel::bncModel(QByteArray staID) {
146
147 _staID = staID;
148
149 connect(this, SIGNAL(newMessage(QByteArray,bool)),
150 ((bncApp*)qApp), SLOT(slotMessage(const QByteArray,bool)));
151
152 bncSettings settings;
153
154 _static = false;
155 if ( Qt::CheckState(settings.value("pppStatic").toInt()) == Qt::Checked) {
156 _static = true;
157 }
158
159 _usePhase = false;
160 if ( Qt::CheckState(settings.value("pppUsePhase").toInt()) == Qt::Checked) {
161 _usePhase = true;
162 }
163
164 _estTropo = false;
165 if ( Qt::CheckState(settings.value("pppEstTropo").toInt()) == Qt::Checked) {
166 _estTropo = true;
167 }
168
169 _xcBanc.ReSize(4); _xcBanc = 0.0;
170 _ellBanc.ReSize(3); _ellBanc = 0.0;
171
172 if ( Qt::CheckState(settings.value("pppGLONASS").toInt()) == Qt::Checked) {
173 _useGlonass = true;
174 }
175 else {
176 _useGlonass = false;
177 }
178
179 int nextPar = 0;
180 _params.push_back(new bncParam(bncParam::CRD_X, ++nextPar, ""));
181 _params.push_back(new bncParam(bncParam::CRD_Y, ++nextPar, ""));
182 _params.push_back(new bncParam(bncParam::CRD_Z, ++nextPar, ""));
183 _params.push_back(new bncParam(bncParam::RECCLK_GPS, ++nextPar, ""));
184 if (_useGlonass) {
185 _params.push_back(new bncParam(bncParam::RECCLK_GLO, ++nextPar, ""));
186 }
187 if (_estTropo) {
188 _params.push_back(new bncParam(bncParam::TROPO, ++nextPar, ""));
189 }
190
191 unsigned nPar = _params.size();
192
193 _QQ.ReSize(nPar);
194
195 _QQ = 0.0;
196
197 for (int iPar = 1; iPar <= _params.size(); iPar++) {
198 bncParam* pp = _params[iPar-1];
199 if (pp->isCrd()) {
200 _QQ(iPar,iPar) = sig_crd_0 * sig_crd_0;
201 }
202 else if (pp->isClk()) {
203 _QQ(iPar,iPar) = sig_clk_0 * sig_clk_0;
204 }
205 else if (pp->type == bncParam::TROPO) {
206 _QQ(iPar,iPar) = sig_trp_0 * sig_trp_0;
207 }
208 }
209
210 // NMEA Output
211 // -----------
212 QString nmeaFileName = settings.value("nmeaFile").toString();
213 if (nmeaFileName.isEmpty()) {
214 _nmeaFile = 0;
215 _nmeaStream = 0;
216 }
217 else {
218 expandEnvVar(nmeaFileName);
219 _nmeaFile = new QFile(nmeaFileName);
220 if ( Qt::CheckState(settings.value("rnxAppend").toInt()) == Qt::Checked) {
221 _nmeaFile->open(QIODevice::WriteOnly | QIODevice::Append);
222 }
223 else {
224 _nmeaFile->open(QIODevice::WriteOnly);
225 }
226 _nmeaStream = new QTextStream();
227 _nmeaStream->setDevice(_nmeaFile);
228 QDateTime dateTime = QDateTime::currentDateTime().toUTC();
229 QString nmStr = "GPRMC," + dateTime.time().toString("hhmmss")
230 + ",A,,,,,,,"
231 + dateTime.date().toString("ddMMyy")
232 + ",,";
233
234 writeNMEAstr(nmStr);
235 }
236}
237
238// Destructor
239////////////////////////////////////////////////////////////////////////////
240bncModel::~bncModel() {
241 delete _nmeaStream;
242 delete _nmeaFile;
243}
244
245// Bancroft Solution
246////////////////////////////////////////////////////////////////////////////
247t_irc bncModel::cmpBancroft(t_epoData* epoData) {
248
249 if (epoData->sizeGPS() < MINOBS) {
250 _log += "\nNot enough data";
251 return failure;
252 }
253
254 Matrix BB(epoData->sizeGPS(), 4);
255
256 QMapIterator<QString, t_satData*> it(epoData->satDataGPS);
257 int iObs = 0;
258 while (it.hasNext()) {
259 ++iObs;
260 it.next();
261 QString prn = it.key();
262 t_satData* satData = it.value();
263 BB(iObs, 1) = satData->xx(1);
264 BB(iObs, 2) = satData->xx(2);
265 BB(iObs, 3) = satData->xx(3);
266 BB(iObs, 4) = satData->P3 + satData->clk;
267 }
268
269 bancroft(BB, _xcBanc);
270
271 // Ellipsoidal Coordinates
272 // ------------------------
273 xyz2ell(_xcBanc.data(), _ellBanc.data());
274
275 // Compute Satellite Elevations
276 // ----------------------------
277 QMutableMapIterator<QString, t_satData*> iGPS(epoData->satDataGPS);
278 while (iGPS.hasNext()) {
279 iGPS.next();
280 QString prn = iGPS.key();
281 t_satData* satData = iGPS.value();
282
283 ColumnVector rr = satData->xx - _xcBanc.Rows(1,3);
284 double rho = rr.norm_Frobenius();
285
286 double neu[3];
287 xyz2neu(_ellBanc.data(), rr.data(), neu);
288
289 satData->eleSat = acos( sqrt(neu[0]*neu[0] + neu[1]*neu[1]) / rho );
290 if (neu[2] < 0) {
291 satData->eleSat *= -1.0;
292 }
293 satData->azSat = atan2(neu[1], neu[0]);
294
295 if (satData->eleSat < MINELE) {
296 delete satData;
297 iGPS.remove();
298 }
299 }
300
301 QMutableMapIterator<QString, t_satData*> iGlo(epoData->satDataGlo);
302 while (iGlo.hasNext()) {
303 iGlo.next();
304 QString prn = iGlo.key();
305 t_satData* satData = iGlo.value();
306
307 ColumnVector rr = satData->xx - _xcBanc.Rows(1,3);
308 double rho = rr.norm_Frobenius();
309
310 double neu[3];
311 xyz2neu(_ellBanc.data(), rr.data(), neu);
312
313 satData->eleSat = acos( sqrt(neu[0]*neu[0] + neu[1]*neu[1]) / rho );
314 if (neu[2] < 0) {
315 satData->eleSat *= -1.0;
316 }
317 satData->azSat = atan2(neu[1], neu[0]);
318
319 if (satData->eleSat < MINELE) {
320 delete satData;
321 iGlo.remove();
322 }
323 }
324
325 return success;
326}
327
328// Computed Value
329////////////////////////////////////////////////////////////////////////////
330double bncModel::cmpValue(t_satData* satData) {
331
332 ColumnVector xRec(3);
333 xRec(1) = x();
334 xRec(2) = y();
335 xRec(3) = z();
336
337 double rho0 = (satData->xx - xRec).norm_Frobenius();
338 double dPhi = t_CST::omega * rho0 / t_CST::c;
339
340 xRec(1) = x() * cos(dPhi) - y() * sin(dPhi);
341 xRec(2) = y() * cos(dPhi) + x() * sin(dPhi);
342 xRec(3) = z();
343
344 satData->rho = (satData->xx - xRec).norm_Frobenius();
345
346 double tropDelay = delay_saast(satData->eleSat) +
347 trp() / sin(satData->eleSat);
348
349 double clk = 0.0;
350 if (satData->prn[0] == 'G') {
351 clk = clkGPS();
352 }
353 else if (satData->prn[0] == 'R') {
354 clk = clkGlo();
355 }
356
357 return satData->rho + clk - satData->clk + tropDelay;
358}
359
360// Tropospheric Model (Saastamoinen)
361////////////////////////////////////////////////////////////////////////////
362double bncModel::delay_saast(double Ele) {
363
364 double height = _ellBanc(3);
365
366 double pp = 1013.25 * pow(1.0 - 2.26e-5 * height, 5.225);
367 double TT = 18.0 - height * 0.0065 + 273.15;
368 double hh = 50.0 * exp(-6.396e-4 * height);
369 double ee = hh / 100.0 * exp(-37.2465 + 0.213166*TT - 0.000256908*TT*TT);
370
371 double h_km = height / 1000.0;
372
373 if (h_km < 0.0) h_km = 0.0;
374 if (h_km > 5.0) h_km = 5.0;
375 int ii = int(h_km + 1);
376 double href = ii - 1;
377
378 double bCor[6];
379 bCor[0] = 1.156;
380 bCor[1] = 1.006;
381 bCor[2] = 0.874;
382 bCor[3] = 0.757;
383 bCor[4] = 0.654;
384 bCor[5] = 0.563;
385
386 double BB = bCor[ii-1] + (bCor[ii]-bCor[ii-1]) * (h_km - href);
387
388 double zen = M_PI/2.0 - Ele;
389
390 return (0.002277/cos(zen)) * (pp + ((1255.0/TT)+0.05)*ee - BB*(tan(zen)*tan(zen)));
391}
392
393// Prediction Step of the Filter
394////////////////////////////////////////////////////////////////////////////
395void bncModel::predict(t_epoData* epoData) {
396
397 bool firstCrd = x() == 0.0 && y() == 0.0 && z() == 0.0;
398
399 // Predict Parameter values, add white noise
400 // -----------------------------------------
401 for (int iPar = 1; iPar <= _params.size(); iPar++) {
402 bncParam* pp = _params[iPar-1];
403
404 // Coordinates
405 // -----------
406 if (pp->type == bncParam::CRD_X) {
407 if (firstCrd || !_static) {
408 pp->xx = _xcBanc(1);
409 }
410 _QQ(iPar,iPar) += sig_crd_p * sig_crd_p;
411 }
412 else if (pp->type == bncParam::CRD_Y) {
413 if (firstCrd || !_static) {
414 pp->xx = _xcBanc(2);
415 }
416 _QQ(iPar,iPar) += sig_crd_p * sig_crd_p;
417 }
418 else if (pp->type == bncParam::CRD_Z) {
419 if (firstCrd || !_static) {
420 pp->xx = _xcBanc(3);
421 }
422 _QQ(iPar,iPar) += sig_crd_p * sig_crd_p;
423 }
424
425 // Receiver Clocks
426 // ---------------
427 else if (pp->isClk()) {
428 pp->xx = _xcBanc(4);
429 for (int jj = 1; jj <= _params.size(); jj++) {
430 _QQ(iPar, jj) = 0.0;
431 }
432 _QQ(iPar,iPar) = sig_clk_0 * sig_clk_0;
433 }
434
435 // Tropospheric Delay
436 // ------------------
437 else if (pp->type == bncParam::TROPO) {
438 _QQ(iPar,iPar) += sig_trp_p * sig_trp_p;
439 }
440 }
441
442 // Add New Ambiguities if necessary
443 // --------------------------------
444 if (_usePhase) {
445
446 // Make a copy of QQ and xx, set parameter indices
447 // -----------------------------------------------
448 SymmetricMatrix QQ_old = _QQ;
449
450 for (int iPar = 1; iPar <= _params.size(); iPar++) {
451 _params[iPar-1]->index_old = _params[iPar-1]->index;
452 _params[iPar-1]->index = 0;
453 }
454
455 // Remove Ambiguity Parameters without observations
456 // ------------------------------------------------
457 int iPar = 0;
458 QMutableVectorIterator<bncParam*> it(_params);
459 while (it.hasNext()) {
460 bncParam* par = it.next();
461 bool removed = false;
462 if (par->type == bncParam::AMB_L3) {
463 if (epoData->satDataGPS.find(par->prn) == epoData->satDataGPS.end() &&
464 epoData->satDataGlo.find(par->prn) == epoData->satDataGlo.end() ) {
465 removed = true;
466 delete par;
467 it.remove();
468 }
469 }
470 if (! removed) {
471 ++iPar;
472 par->index = iPar;
473 }
474 }
475
476 // Add new ambiguity parameters
477 // ----------------------------
478 QMapIterator<QString, t_satData*> iGPS(epoData->satDataGPS);
479 while (iGPS.hasNext()) {
480 iGPS.next();
481 QString prn = iGPS.key();
482 t_satData* satData = iGPS.value();
483 bool found = false;
484 for (int iPar = 1; iPar <= _params.size(); iPar++) {
485 if (_params[iPar-1]->type == bncParam::AMB_L3 &&
486 _params[iPar-1]->prn == prn) {
487 found = true;
488 break;
489 }
490 }
491 if (!found) {
492 bncParam* par = new bncParam(bncParam::AMB_L3, _params.size()+1, prn);
493 _params.push_back(par);
494 par->xx = satData->L3 - cmpValue(satData);
495 }
496 }
497
498 QMapIterator<QString, t_satData*> iGlo(epoData->satDataGlo);
499 while (iGlo.hasNext()) {
500 iGlo.next();
501 QString prn = iGlo.key();
502 t_satData* satData = iGlo.value();
503 bool found = false;
504 for (int iPar = 1; iPar <= _params.size(); iPar++) {
505 if (_params[iPar-1]->type == bncParam::AMB_L3 &&
506 _params[iPar-1]->prn == prn) {
507 found = true;
508 break;
509 }
510 }
511 if (!found) {
512 bncParam* par = new bncParam(bncParam::AMB_L3, _params.size()+1, prn);
513 _params.push_back(par);
514 par->xx = satData->L3 - cmpValue(satData);
515 }
516 }
517
518 int nPar = _params.size();
519 _QQ.ReSize(nPar); _QQ = 0.0;
520 for (int i1 = 1; i1 <= nPar; i1++) {
521 bncParam* p1 = _params[i1-1];
522 if (p1->index_old != 0) {
523 _QQ(p1->index, p1->index) = QQ_old(p1->index_old, p1->index_old);
524 for (int i2 = 1; i2 <= nPar; i2++) {
525 bncParam* p2 = _params[i2-1];
526 if (p2->index_old != 0) {
527 _QQ(p1->index, p2->index) = QQ_old(p1->index_old, p2->index_old);
528 }
529 }
530 }
531 }
532
533 for (int ii = 1; ii <= nPar; ii++) {
534 bncParam* par = _params[ii-1];
535 if (par->index_old == 0) {
536 _QQ(par->index, par->index) = sig_amb_0 * sig_amb_0;
537 }
538 par->index_old = par->index;
539 }
540 }
541
542}
543
544// Update Step of the Filter (currently just a single-epoch solution)
545////////////////////////////////////////////////////////////////////////////
546t_irc bncModel::update(t_epoData* epoData) {
547
548 _log = "Precise Point Positioning";
549
550 _time = epoData->tt;
551
552 SymmetricMatrix QQsav;
553 ColumnVector dx;
554 ColumnVector vv;
555
556 // Loop over all outliers
557 // ----------------------
558 do {
559
560 // Bancroft Solution
561 // -----------------
562 if (cmpBancroft(epoData) != success) {
563 _log += "\nBancroft failed";
564 emit newMessage(_log, false);
565 return failure;
566 }
567
568 if (epoData->sizeGPS() < MINOBS) {
569 _log += "\nNot enough data";
570 emit newMessage(_log, false);
571 return failure;
572 }
573
574 // Status Prediction
575 // -----------------
576 predict(epoData);
577
578 // Create First-Design Matrix
579 // --------------------------
580 unsigned nPar = _params.size();
581 unsigned nObs = 0;
582 if (_usePhase) {
583 nObs = 2 * epoData->sizeGPS() + epoData->sizeGlo();
584 }
585 else {
586 nObs = epoData->sizeGPS(); // Glonass pseudoranges are not used
587 }
588
589 Matrix AA(nObs, nPar); // first design matrix
590 ColumnVector ll(nObs); // tems observed-computed
591 SymmetricMatrix PP(nObs); PP = 0.0;
592
593 unsigned iObs = 0;
594
595 // GPS code and (optionally) phase observations
596 // --------------------------------------------
597 QMapIterator<QString, t_satData*> itGPS(epoData->satDataGPS);
598 while (itGPS.hasNext()) {
599 ++iObs;
600 itGPS.next();
601 QString prn = itGPS.key();
602 t_satData* satData = itGPS.value();
603
604 double rhoCmp = cmpValue(satData);
605
606 ll(iObs) = satData->P3 - rhoCmp;
607 PP(iObs,iObs) = 1.0 / (sig_P3 * sig_P3);
608 for (int iPar = 1; iPar <= _params.size(); iPar++) {
609 AA(iObs, iPar) = _params[iPar-1]->partial(satData, false);
610 }
611
612 if (_usePhase) {
613 ++iObs;
614 ll(iObs) = satData->L3 - rhoCmp;
615 PP(iObs,iObs) = 1.0 / (sig_L3_GPS * sig_L3_GPS);
616 for (int iPar = 1; iPar <= _params.size(); iPar++) {
617 if (_params[iPar-1]->type == bncParam::AMB_L3 &&
618 _params[iPar-1]->prn == prn) {
619 ll(iObs) -= _params[iPar-1]->xx;
620 }
621 AA(iObs, iPar) = _params[iPar-1]->partial(satData, true);
622 }
623 }
624 }
625
626 // Glonass phase observations
627 // --------------------------
628 if (_usePhase) {
629 QMapIterator<QString, t_satData*> itGlo(epoData->satDataGlo);
630 while (itGlo.hasNext()) {
631 ++iObs;
632 itGlo.next();
633 QString prn = itGlo.key();
634 t_satData* satData = itGlo.value();
635
636 double rhoCmp = cmpValue(satData);
637
638 ll(iObs) = satData->L3 - rhoCmp;
639
640 PP(iObs,iObs) = 1.0 / (sig_L3_GLO * sig_L3_GLO);
641 for (int iPar = 1; iPar <= _params.size(); iPar++) {
642 if (_params[iPar-1]->type == bncParam::AMB_L3 &&
643 _params[iPar-1]->prn == prn) {
644 ll(iObs) -= _params[iPar-1]->xx;
645 }
646 AA(iObs, iPar) = _params[iPar-1]->partial(satData, true);
647 }
648
649 ostringstream str;
650 str.setf(ios::fixed);
651 str << "iObs = " << iObs << " " << prn.toAscii().data() << " "
652 << setprecision(3) << rhoCmp << " "
653 << setprecision(3) << satData->P3 << " "
654 << setprecision(3) << satData->L3 << " "
655 << ll(iObs) << endl;
656 _log += str.str().c_str();
657 }
658 }
659
660 // Compute Filter Update
661 // ---------------------
662 QQsav = _QQ;
663
664 Matrix ATP = AA.t() * PP;
665 SymmetricMatrix NN = _QQ.i();
666 NN << NN + ATP * AA;
667 _QQ = NN.i();
668 dx = _QQ * ATP * ll;
669 vv = ll - AA * dx;
670
671 //// beg test
672 {
673 ostringstream str;
674 str.setf(ios::fixed);
675 ColumnVector vv_code(epoData->sizeGPS());
676 ColumnVector vv_phase(epoData->sizeGPS());
677 ColumnVector vv_glo(epoData->sizeGlo());
678
679 for (unsigned iobs = 1; iobs <= epoData->sizeGPS(); ++iobs) {
680 vv_code(iobs) = vv(2*iobs-1);
681 vv_phase(iobs) = vv(2*iobs);
682 }
683 for (unsigned iobs = 1; iobs <= epoData->sizeGlo(); ++iobs) {
684 vv_glo(iobs) = vv(2*epoData->sizeGPS()+iobs);
685 }
686
687 str << "residuals code " << setprecision(3) << vv_code.t();
688 str << "residuals phase " << setprecision(3) << vv_phase.t();
689 str << "residuals glo " << setprecision(3) << vv_glo.t();
690 _log += str.str().c_str();
691 }
692 //// end test
693
694 } while (outlierDetection(QQsav, vv, epoData->satDataGPS,
695 epoData->satDataGlo) != 0);
696
697 // Set Solution Vector
698 // -------------------
699 ostringstream str1;
700 str1.setf(ios::fixed);
701 QVectorIterator<bncParam*> itPar(_params);
702 while (itPar.hasNext()) {
703 bncParam* par = itPar.next();
704 par->xx += dx(par->index);
705 if (par->type == bncParam::RECCLK_GPS) {
706 str1 << "\n clk GPS = " << setw(6) << setprecision(3) << par->xx
707 << " +- " << setw(6) << setprecision(3)
708 << sqrt(_QQ(par->index,par->index));
709 }
710 if (par->type == bncParam::RECCLK_GLO) {
711 str1 << "\n clk GLO = " << setw(6) << setprecision(3) << par->xx
712 << " +- " << setw(6) << setprecision(3)
713 << sqrt(_QQ(par->index,par->index));
714 }
715 else if (par->type == bncParam::AMB_L3) {
716 str1 << "\n amb " << par->prn.toAscii().data() << " = "
717 << setw(6) << setprecision(3) << par->xx
718 << " +- " << setw(6) << setprecision(3)
719 << sqrt(_QQ(par->index,par->index));
720 }
721 else if (par->type == bncParam::TROPO) {
722 str1 << "\n trp = " << par->prn.toAscii().data()
723 << setw(7) << setprecision(3) << delay_saast(M_PI/2.0) << " "
724 << setw(6) << setprecision(3) << showpos << par->xx << noshowpos
725 << " +- " << setw(6) << setprecision(3)
726 << sqrt(_QQ(par->index,par->index));
727 }
728 }
729 _log += str1.str().c_str();
730
731 // Message (both log file and screen)
732 // ----------------------------------
733 ostringstream str2;
734 str2.setf(ios::fixed);
735 str2 << _staID.data() << ": PPP "
736 << epoData->tt.timestr(1) << " " << epoData->sizeAll() << " "
737 << setw(14) << setprecision(3) << x() << " +- "
738 << setw(6) << setprecision(3) << sqrt(_QQ(1,1)) << " "
739 << setw(14) << setprecision(3) << y() << " +- "
740 << setw(6) << setprecision(3) << sqrt(_QQ(2,2)) << " "
741 << setw(14) << setprecision(3) << z() << " +- "
742 << setw(6) << setprecision(3) << sqrt(_QQ(3,3));
743
744 emit newMessage(_log, false);
745 emit newMessage(QByteArray(str2.str().c_str()), true);
746
747 // NMEA Output
748 // -----------
749 double xyz[3];
750 xyz[0] = x();
751 xyz[1] = y();
752 xyz[2] = z();
753 double ell[3];
754 xyz2ell(xyz, ell);
755 double phiDeg = ell[0] * 180 / M_PI;
756 double lamDeg = ell[1] * 180 / M_PI;
757
758 char phiCh = 'N';
759 if (phiDeg < 0) {
760 phiDeg = -phiDeg;
761 phiCh = 'S';
762 }
763 char lamCh = 'E';
764 if (lamDeg < 0) {
765 lamDeg = -lamDeg;
766 lamCh = 'W';
767 }
768
769 double dop = 2.0; // TODO
770
771 ostringstream str3;
772 str3.setf(ios::fixed);
773 str3 << "GPGGA,"
774 << epoData->tt.timestr(0,0) << ','
775 << setw(2) << setfill('0') << int(phiDeg)
776 << setw(10) << setprecision(7) << setfill('0')
777 << fmod(60*phiDeg,60) << ',' << phiCh << ','
778 << setw(2) << setfill('0') << int(lamDeg)
779 << setw(10) << setprecision(7) << setfill('0')
780 << fmod(60*lamDeg,60) << ',' << lamCh
781 << ",1," << setw(2) << setfill('0') << epoData->sizeAll() << ','
782 << setw(3) << setprecision(1) << dop << ','
783 << setprecision(3) << ell[2] << ",M,0.0,M,,,";
784
785 writeNMEAstr(QString(str3.str().c_str()));
786
787 return success;
788}
789
790// Outlier Detection
791////////////////////////////////////////////////////////////////////////////
792int bncModel::outlierDetection(const SymmetricMatrix& QQsav,
793 const ColumnVector& vv,
794 QMap<QString, t_satData*>& satDataGPS,
795 QMap<QString, t_satData*>& satDataGlo) {
796
797 double vvMaxCodeGPS = 0.0;
798 double vvMaxPhaseGPS = 0.0;
799 double vvMaxPhaseGlo = 0.0;
800 QMutableMapIterator<QString, t_satData*> itMaxCodeGPS(satDataGPS);
801 QMutableMapIterator<QString, t_satData*> itMaxPhaseGPS(satDataGPS);
802 QMutableMapIterator<QString, t_satData*> itMaxPhaseGlo(satDataGlo);
803
804 int ii = 0;
805
806 // GPS code and (optionally) phase residuals
807 // -----------------------------------------
808 QMutableMapIterator<QString, t_satData*> itGPS(satDataGPS);
809 while (itGPS.hasNext()) {
810 itGPS.next();
811 ++ii;
812
813 if (vvMaxCodeGPS == 0.0 || fabs(vv(ii)) > vvMaxCodeGPS) {
814 vvMaxCodeGPS = fabs(vv(ii));
815 itMaxCodeGPS = itGPS;
816 }
817
818 if (_usePhase) {
819 ++ii;
820 if (vvMaxPhaseGPS == 0.0 || fabs(vv(ii)) > vvMaxPhaseGPS) {
821 vvMaxPhaseGPS = fabs(vv(ii));
822 itMaxPhaseGPS = itGPS;
823 }
824 }
825 }
826
827 // Glonass phase residuals
828 // -----------------------
829 if (_usePhase) {
830 QMutableMapIterator<QString, t_satData*> itGlo(satDataGlo);
831 while (itGlo.hasNext()) {
832 itGlo.next();
833 ++ii;
834 if (vvMaxPhaseGlo == 0.0 || fabs(vv(ii)) > vvMaxPhaseGlo) {
835 vvMaxPhaseGlo = fabs(vv(ii));
836 itMaxPhaseGlo = itGlo;
837 }
838 }
839 }
840
841 if (vvMaxCodeGPS > MAXRES_CODE_GPS) {
842 QString prn = itMaxCodeGPS.key();
843 t_satData* satData = itMaxCodeGPS.value();
844 delete satData;
845 itMaxCodeGPS.remove();
846 _QQ = QQsav;
847
848 _log += "\nOutlier Code " + prn.toAscii() + " "
849 + QByteArray::number(vvMaxCodeGPS, 'f', 3);
850
851 return 1;
852 }
853 else if (vvMaxPhaseGPS > MAXRES_PHASE_GPS) {
854 QString prn = itMaxPhaseGPS.key();
855 t_satData* satData = itMaxPhaseGPS.value();
856 delete satData;
857 itMaxPhaseGPS.remove();
858 _QQ = QQsav;
859
860 _log += "\nOutlier Phase " + prn.toAscii() + " "
861 + QByteArray::number(vvMaxPhaseGPS, 'f', 3);
862
863 return 1;
864 }
865 else if (vvMaxPhaseGlo > MAXRES_PHASE_GLO) {
866 QString prn = itMaxPhaseGlo.key();
867 t_satData* satData = itMaxPhaseGlo.value();
868 delete satData;
869 itMaxPhaseGlo.remove();
870 _QQ = QQsav;
871
872 _log += "\nOutlier Phase " + prn.toAscii() + " "
873 + QByteArray::number(vvMaxPhaseGlo, 'f', 3);
874
875 return 1;
876 }
877
878 return 0;
879}
880
881//
882////////////////////////////////////////////////////////////////////////////
883void bncModel::writeNMEAstr(const QString& nmStr) {
884
885 unsigned char XOR = 0;
886 for (int ii = 0; ii < nmStr.length(); ii++) {
887 XOR ^= (unsigned char) nmStr[ii].toAscii();
888 }
889
890 QString outStr = '$' + nmStr
891 + QString("*%1\n").arg(int(XOR), 0, 16).toUpper();
892
893 if (_nmeaStream) {
894 *_nmeaStream << outStr;
895 _nmeaStream->flush();
896 }
897
898 emit newNMEAstr(outStr.toAscii());
899}
Note: See TracBrowser for help on using the repository browser.