source: ntrip/trunk/BNC/src/pppRun.cpp@ 7258

Last change on this file since 7258 was 7231, checked in by stuerze, 10 years ago

some interfaces are added to be able to handle ssr vtec in PPP mode

File size: 17.1 KB
Line 
1
2// Part of BNC, a utility for retrieving decoding and
3// converting GNSS data streams from NTRIP broadcasters.
4//
5// Copyright (C) 2007
6// German Federal Agency for Cartography and Geodesy (BKG)
7// http://www.bkg.bund.de
8// Czech Technical University Prague, Department of Geodesy
9// http://www.fsv.cvut.cz
10//
11// Email: euref-ip@bkg.bund.de
12//
13// This program is free software; you can redistribute it and/or
14// modify it under the terms of the GNU General Public License
15// as published by the Free Software Foundation, version 2.
16//
17// This program is distributed in the hope that it will be useful,
18// but WITHOUT ANY WARRANTY; without even the implied warranty of
19// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20// GNU General Public License for more details.
21//
22// You should have received a copy of the GNU General Public License
23// along with this program; if not, write to the Free Software
24// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25
26/* -------------------------------------------------------------------------
27 * BKG NTRIP Client
28 * -------------------------------------------------------------------------
29 *
30 * Class: t_pppRun
31 *
32 * Purpose: Single Real-Time PPP Client
33 *
34 * Author: L. Mervart
35 *
36 * Created: 29-Jul-2014
37 *
38 * Changes:
39 *
40 * -----------------------------------------------------------------------*/
41
42
43#include <iostream>
44#include <iomanip>
45#include <sstream>
46#include <string.h>
47#include <map>
48
49#include "pppRun.h"
50#include "pppThread.h"
51#include "bnccore.h"
52#include "bncephuser.h"
53#include "bncsettings.h"
54#include "bncoutf.h"
55#include "bncsinextro.h"
56#include "rinex/rnxobsfile.h"
57#include "rinex/rnxnavfile.h"
58#include "rinex/corrfile.h"
59
60using namespace BNC_PPP;
61using namespace std;
62
63// Constructor
64////////////////////////////////////////////////////////////////////////////
65t_pppRun::t_pppRun(const t_pppOptions* opt) {
66
67 _opt = opt;
68
69 connect(this, SIGNAL(newMessage(QByteArray,bool)),
70 BNC_CORE, SLOT(slotMessage(const QByteArray,bool)));
71
72 connect(this, SIGNAL(newPosition(QByteArray, bncTime, QVector<double>)),
73 BNC_CORE, SIGNAL(newPosition(QByteArray, bncTime, QVector<double>)));
74
75 connect(this, SIGNAL(newNMEAstr(QByteArray, QByteArray)),
76 BNC_CORE, SIGNAL(newNMEAstr(QByteArray, QByteArray)));
77
78 _pppClient = new t_pppClient(_opt);
79
80 bncSettings settings;
81
82 if (_opt->_realTime) {
83 Qt::ConnectionType conType = Qt::AutoConnection;
84 if (BNC_CORE->mode() == t_bncCore::batchPostProcessing) {
85 conType = Qt::BlockingQueuedConnection;
86 }
87
88 connect(BNC_CORE->caster(), SIGNAL(newObs(QByteArray, QList<t_satObs>)),
89 this, SLOT(slotNewObs(QByteArray, QList<t_satObs>)),conType);
90
91 connect(BNC_CORE, SIGNAL(newGPSEph(t_ephGPS)),
92 this, SLOT(slotNewGPSEph(t_ephGPS)),conType);
93
94 connect(BNC_CORE, SIGNAL(newGlonassEph(t_ephGlo)),
95 this, SLOT(slotNewGlonassEph(t_ephGlo)),conType);
96
97 connect(BNC_CORE, SIGNAL(newGalileoEph(t_ephGal)),
98 this, SLOT(slotNewGalileoEph(t_ephGal)),conType);
99
100 connect(BNC_CORE, SIGNAL(newBDSEph(t_ephBDS)),
101 this, SLOT(slotNewBDSEph(t_ephBDS)),conType);
102
103 connect(BNC_CORE, SIGNAL(newTec(t_vTec)),
104 this, SLOT(slotNewTec(t_vTec)),conType);
105
106 connect(BNC_CORE, SIGNAL(newOrbCorrections(QList<t_orbCorr>)),
107 this, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)),conType);
108
109 connect(BNC_CORE, SIGNAL(newClkCorrections(QList<t_clkCorr>)),
110 this, SLOT(slotNewClkCorrections(QList<t_clkCorr>)),conType);
111
112 connect(BNC_CORE, SIGNAL(newCodeBiases(QList<t_satCodeBias>)),
113 this, SLOT(slotNewCodeBiases(QList<t_satCodeBias>)),conType);
114 }
115 else {
116 _rnxObsFile = 0;
117 _rnxNavFile = 0;
118 _corrFile = 0;
119 _speed = settings.value("PPP/mapSpeedSlider").toInt();
120 connect(this, SIGNAL(progressRnxPPP(int)), BNC_CORE, SIGNAL(progressRnxPPP(int)));
121 connect(this, SIGNAL(finishedRnxPPP()), BNC_CORE, SIGNAL(finishedRnxPPP()));
122 connect(BNC_CORE, SIGNAL(mapSpeedSliderChanged(int)),
123 this, SLOT(slotSetSpeed(int)));
124 connect(BNC_CORE, SIGNAL(stopRinexPPP()), this, SLOT(slotSetStopFlag()));
125 }
126
127 _stopFlag = false;
128
129 QString roverName(_opt->_roverName.c_str());
130
131 QString logFileSkl = settings.value("PPP/logFilePPP").toString();
132 if (logFileSkl.isEmpty()) {
133 _logFile = 0;
134 }
135 else {
136 if (logFileSkl.indexOf("${STATION}") == -1) {
137 logFileSkl = logFileSkl + "_" + roverName;
138 }
139 else {
140 logFileSkl.replace("${STATION}", roverName);
141 }
142 _logFile = new bncoutf(logFileSkl, "1 day", 0);
143 }
144
145 QString nmeaFileSkl = settings.value("PPP/nmeaFile").toString();
146 if (nmeaFileSkl.isEmpty()) {
147 _nmeaFile = 0;
148 }
149 else {
150 if (nmeaFileSkl.indexOf("${STATION}") == -1) {
151 nmeaFileSkl = roverName + "_" + nmeaFileSkl;
152 }
153 else {
154 nmeaFileSkl.replace("${STATION}", roverName);
155 }
156 _nmeaFile = new bncoutf(nmeaFileSkl, "1 day", 0);
157 }
158
159 QString snxtroFileSkl = settings.value("PPP/snxtroFile").toString();
160 if (snxtroFileSkl.isEmpty()) {
161 _snxtroFile = 0;
162 }
163 else {
164 if (snxtroFileSkl.indexOf("${STATION}") == -1) {
165 snxtroFileSkl = snxtroFileSkl + "_" + roverName;
166 }
167 else {
168 snxtroFileSkl.replace("${STATION}", roverName.left(4));
169 }
170 int samplSnxTro = settings.value("PPP/snxtroSampl").toInt();
171 _snxtroFile = new bncSinexTro(_opt, snxtroFileSkl, "1 day", samplSnxTro);
172 }
173}
174
175
176
177// Destructor
178////////////////////////////////////////////////////////////////////////////
179t_pppRun::~t_pppRun() {
180 delete _logFile;
181 delete _nmeaFile;
182 delete _snxtroFile;
183}
184
185//
186////////////////////////////////////////////////////////////////////////////
187void t_pppRun::slotNewGPSEph(t_ephGPS eph) {
188 QMutexLocker locker(&_mutex);
189 _pppClient->putEphemeris(&eph);
190}
191
192//
193////////////////////////////////////////////////////////////////////////////
194void t_pppRun::slotNewGlonassEph(t_ephGlo eph) {
195 QMutexLocker locker(&_mutex);
196 _pppClient->putEphemeris(&eph);
197}
198
199//
200////////////////////////////////////////////////////////////////////////////
201void t_pppRun::slotNewGalileoEph(t_ephGal eph) {
202 QMutexLocker locker(&_mutex);
203 _pppClient->putEphemeris(&eph);
204}
205
206//
207////////////////////////////////////////////////////////////////////////////
208void t_pppRun::slotNewBDSEph(t_ephBDS eph) {
209 QMutexLocker locker(&_mutex);
210 _pppClient->putEphemeris(&eph);
211}
212
213//
214////////////////////////////////////////////////////////////////////////////
215void t_pppRun::slotNewObs(QByteArray staID, QList<t_satObs> obsList) {
216 QMutexLocker locker(&_mutex);
217
218 if (string(staID.data()) != _opt->_roverName) {
219 return;
220 }
221
222 // Loop over all obsevations (possible different epochs)
223 // -----------------------------------------------------
224 QListIterator<t_satObs> it(obsList);
225 while (it.hasNext()) {
226 const t_satObs& oldObs = it.next();
227 t_satObs* newObs = new t_satObs(oldObs);
228
229 // Find the corresponding data epoch or create a new one
230 // -----------------------------------------------------
231 t_epoData* epoch = 0;
232 deque<t_epoData*>::const_iterator it;
233 for (it = _epoData.begin(); it != _epoData.end(); it++) {
234 if (newObs->_time == (*it)->_time) {
235 epoch = *it;
236 break;
237 }
238 }
239 if (epoch == 0) {
240 if (_epoData.empty() || newObs->_time > _epoData.back()->_time) {
241 epoch = new t_epoData;
242 epoch->_time = newObs->_time;
243 _epoData.push_back(epoch);
244 }
245 }
246
247 // Put data into the epoch
248 // -----------------------
249 if (epoch != 0) {
250 epoch->_satObs.push_back(newObs);
251 }
252 else {
253 delete newObs;
254 }
255 }
256
257 // Process the oldest epochs
258 // ------------------------
259 while (_epoData.size() && !waitForCorr(_epoData.front()->_time)) {
260
261 const vector<t_satObs*>& satObs = _epoData.front()->_satObs;
262
263 t_output output;
264 _pppClient->processEpoch(satObs, &output);
265
266 if (!output._error) {
267 QVector<double> xx(6);
268 xx.data()[0] = output._xyzRover[0];
269 xx.data()[1] = output._xyzRover[1];
270 xx.data()[2] = output._xyzRover[2];
271 xx.data()[3] = output._neu[0];
272 xx.data()[4] = output._neu[1];
273 xx.data()[5] = output._neu[2];
274 emit newPosition(staID, output._epoTime, xx);
275 }
276
277 delete _epoData.front(); _epoData.pop_front();
278
279 ostringstream log;
280 if (output._error) {
281 log << output._log;
282 }
283 else {
284 log.setf(ios::fixed);
285 log << string(output._epoTime) << ' ' << staID.data()
286 << " X = " << setprecision(4) << output._xyzRover[0]
287 << " Y = " << setprecision(4) << output._xyzRover[1]
288 << " Z = " << setprecision(4) << output._xyzRover[2]
289 << " NEU: " << showpos << setw(8) << setprecision(4) << output._neu[0]
290 << " " << showpos << setw(8) << setprecision(4) << output._neu[1]
291 << " " << showpos << setw(8) << setprecision(4) << output._neu[2]
292 << " TRP: " << showpos << setw(8) << setprecision(4) << output._trp0
293 << " " << showpos << setw(8) << setprecision(4) << output._trp;
294 }
295
296 if (_logFile && output._epoTime.valid()) {
297 _logFile->write(output._epoTime.gpsw(), output._epoTime.gpssec(),
298 QString(output._log.c_str()));
299 }
300
301 if (!output._error) {
302 QString rmcStr = nmeaString('R', output);
303 QString ggaStr = nmeaString('G', output);
304 if (_nmeaFile) {
305 _nmeaFile->write(output._epoTime.gpsw(), output._epoTime.gpssec(), rmcStr);
306 _nmeaFile->write(output._epoTime.gpsw(), output._epoTime.gpssec(), ggaStr);
307 }
308 emit newNMEAstr(staID, rmcStr.toAscii());
309 emit newNMEAstr(staID, ggaStr.toAscii());
310 }
311
312 if (_snxtroFile && output._epoTime.valid()) {
313 _snxtroFile->write(staID, int(output._epoTime.gpsw()), output._epoTime.gpssec(),
314 output._trp0 + output._trp, output._trpStdev);
315 }
316
317 emit newMessage(QByteArray(log.str().c_str()), true);
318 }
319}
320
321//
322////////////////////////////////////////////////////////////////////////////
323void t_pppRun::slotNewTec(t_vTec vTec) {
324 if (vTec._layers.size() == 0) {
325 return;
326 }
327
328 if (_opt->_realTime) {
329 if (_opt->_corrMount.empty() || _opt->_corrMount != vTec._staID) {
330 return;
331 }
332 }
333
334 _pppClient->putTec(&vTec);
335}
336
337//
338////////////////////////////////////////////////////////////////////////////
339void t_pppRun::slotNewOrbCorrections(QList<t_orbCorr> orbCorr) {
340 if (orbCorr.size() == 0) {
341 return;
342 }
343
344 if (_opt->_realTime) {
345 if (_opt->_corrMount.empty() || _opt->_corrMount != orbCorr[0]._staID) {
346 return;
347 }
348 }
349 vector<t_orbCorr*> corrections;
350 for (int ii = 0; ii < orbCorr.size(); ii++) {
351 corrections.push_back(new t_orbCorr(orbCorr[ii]));
352 }
353
354 _pppClient->putOrbCorrections(corrections);
355}
356
357//
358////////////////////////////////////////////////////////////////////////////
359void t_pppRun::slotNewClkCorrections(QList<t_clkCorr> clkCorr) {
360 if (clkCorr.size() == 0) {
361 return;
362 }
363
364 if (_opt->_realTime) {
365 if (_opt->_corrMount.empty() || _opt->_corrMount != clkCorr[0]._staID) {
366 return;
367 }
368 }
369 vector<t_clkCorr*> corrections;
370 for (int ii = 0; ii < clkCorr.size(); ii++) {
371 corrections.push_back(new t_clkCorr(clkCorr[ii]));
372 _lastClkCorrTime = clkCorr[ii]._time;
373 }
374 _pppClient->putClkCorrections(corrections);
375}
376
377//
378////////////////////////////////////////////////////////////////////////////
379void t_pppRun::slotNewCodeBiases(QList<t_satCodeBias> codeBiases) {
380 if (codeBiases.size() == 0) {
381 return;
382 }
383
384 if (_opt->_realTime) {
385 if (_opt->_corrMount.empty() || _opt->_corrMount != codeBiases[0]._staID) {
386 return;
387 }
388 }
389 vector<t_satCodeBias*> biases;
390 for (int ii = 0; ii < codeBiases.size(); ii++) {
391 biases.push_back(new t_satCodeBias(codeBiases[ii]));
392 }
393
394 _pppClient->putCodeBiases(biases);
395}
396
397//
398////////////////////////////////////////////////////////////////////////////
399void t_pppRun::processFiles() {
400
401 try {
402 _rnxObsFile = new t_rnxObsFile(QString(_opt->_rinexObs.c_str()), t_rnxObsFile::input);
403 }
404 catch (...) {
405 delete _rnxObsFile; _rnxObsFile = 0;
406 emit finishedRnxPPP();
407 return;
408 }
409
410 _rnxNavFile = new t_rnxNavFile(QString(_opt->_rinexNav.c_str()), t_rnxNavFile::input);
411
412 if (!_opt->_corrFile.empty()) {
413 _corrFile = new t_corrFile(QString(_opt->_corrFile.c_str()));
414 connect(_corrFile, SIGNAL(newTec(t_vTec)),
415 this, SLOT(slotNewTec(t_vTec)));
416 connect(_corrFile, SIGNAL(newOrbCorrections(QList<t_orbCorr>)),
417 this, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)));
418 connect(_corrFile, SIGNAL(newClkCorrections(QList<t_clkCorr>)),
419 this, SLOT(slotNewClkCorrections(QList<t_clkCorr>)));
420 connect(_corrFile, SIGNAL(newCodeBiases(QList<t_satCodeBias>)),
421 this, SLOT(slotNewCodeBiases(QList<t_satCodeBias>)));
422 }
423
424 // Read/Process Observations
425 // -------------------------
426 int nEpo = 0;
427 const t_rnxObsFile::t_rnxEpo* epo = 0;
428 while ( !_stopFlag && (epo = _rnxObsFile->nextEpoch()) != 0 ) {
429 ++nEpo;
430
431 if (_speed < 100) {
432 double sleepTime = 2.0 / _speed;
433 t_pppThread::msleep(int(sleepTime*1.e3));
434 }
435
436 // Get Corrections
437 // ---------------
438 if (_corrFile) {
439 try {
440 _corrFile->syncRead(epo->tt);
441 }
442 catch (const char* msg) {
443 emit newMessage(QByteArray(msg), true);
444 break;
445 }
446 catch (const string& msg) {
447 emit newMessage(QByteArray(msg.c_str()), true);
448 break;
449 }
450 catch (...) {
451 emit newMessage("unknown exceptions in corrFile", true);
452 break;
453 }
454 }
455
456 // Get Ephemerides
457 // ----------------
458 t_eph* eph = 0;
459 const QMap<QString, unsigned int>* corrIODs = _corrFile ? &_corrFile->corrIODs() : 0;
460 while ( (eph = _rnxNavFile->getNextEph(epo->tt, corrIODs)) != 0 ) {
461 _pppClient->putEphemeris(eph);
462 delete eph; eph = 0;
463 }
464
465 // Create list of observations and start epoch processing
466 // ------------------------------------------------------
467 QList<t_satObs> obsList;
468 for (unsigned iObs = 0; iObs < epo->rnxSat.size(); iObs++) {
469 const t_rnxObsFile::t_rnxSat& rnxSat = epo->rnxSat[iObs];
470
471 t_satObs obs;
472 t_rnxObsFile::setObsFromRnx(_rnxObsFile, epo, rnxSat, obs);
473 obsList << obs;
474 }
475 slotNewObs(QByteArray(_opt->_roverName.c_str()), obsList);
476
477
478 if (nEpo % 10 == 0) {
479 emit progressRnxPPP(nEpo);
480 }
481
482 QCoreApplication::processEvents();
483 }
484
485 emit finishedRnxPPP();
486
487 if (BNC_CORE->mode() != t_bncCore::interactive) {
488 qApp->exit(0);
489 }
490 else {
491 BNC_CORE->stopPPP();
492 }
493}
494
495//
496////////////////////////////////////////////////////////////////////////////
497void t_pppRun::slotSetSpeed(int speed) {
498 QMutexLocker locker(&_mutex);
499 _speed = speed;
500}
501
502//
503////////////////////////////////////////////////////////////////////////////
504void t_pppRun::slotSetStopFlag() {
505 QMutexLocker locker(&_mutex);
506 _stopFlag = true;
507}
508
509//
510////////////////////////////////////////////////////////////////////////////
511QString t_pppRun::nmeaString(char strType, const t_output& output) {
512
513 double ell[3];
514 xyz2ell(output._xyzRover, ell);
515 double phiDeg = ell[0] * 180 / M_PI;
516 double lamDeg = ell[1] * 180 / M_PI;
517
518 char phiCh = 'N';
519 if (phiDeg < 0) {
520 phiDeg = -phiDeg;
521 phiCh = 'S';
522 }
523 char lamCh = 'E';
524 if (lamDeg < 0) {
525 lamDeg = -lamDeg;
526 lamCh = 'W';
527 }
528
529 ostringstream out;
530 out.setf(ios::fixed);
531
532 if (strType == 'R') {
533 string datestr = output._epoTime.datestr(0); // yyyymmdd
534 out << "GPRMC,"
535 << output._epoTime.timestr(0,0) << ",A,"
536 << setw(2) << setfill('0') << int(phiDeg)
537 << setw(6) << setprecision(3) << setfill('0')
538 << fmod(60*phiDeg,60) << ',' << phiCh << ','
539 << setw(3) << setfill('0') << int(lamDeg)
540 << setw(6) << setprecision(3) << setfill('0')
541 << fmod(60*lamDeg,60) << ',' << lamCh << ",,,"
542 << datestr[6] << datestr[7] << datestr[4] << datestr[5]
543 << datestr[2] << datestr[3] << ",,";
544 }
545 else if (strType == 'G') {
546 out << "GPGGA,"
547 << output._epoTime.timestr(0,0) << ','
548 << setw(2) << setfill('0') << int(phiDeg)
549 << setw(10) << setprecision(7) << setfill('0')
550 << fmod(60*phiDeg,60) << ',' << phiCh << ','
551 << setw(3) << setfill('0') << int(lamDeg)
552 << setw(10) << setprecision(7) << setfill('0')
553 << fmod(60*lamDeg,60) << ',' << lamCh
554 << ",1," << setw(2) << setfill('0') << output._numSat << ','
555 << setw(3) << setprecision(1) << output._pDop << ','
556 << setprecision(3) << ell[2] << ",M,0.0,M,,";
557 }
558 else {
559 return "";
560 }
561
562 QString nmStr(out.str().c_str());
563 unsigned char XOR = 0;
564 for (int ii = 0; ii < nmStr.length(); ii++) {
565 XOR ^= (unsigned char) nmStr[ii].toAscii();
566 }
567
568 return '$' + nmStr + QString("*%1\n").arg(int(XOR), 0, 16).toUpper();
569}
570
571//
572////////////////////////////////////////////////////////////////////////////
573bool t_pppRun::waitForCorr(const bncTime& epoTime) const {
574
575 if (!_opt->_realTime || _opt->_corrMount.empty()) {
576 return false;
577 }
578 else if (!_lastClkCorrTime.valid()) {
579 return true;
580 }
581 else {
582 double dt = epoTime - _lastClkCorrTime;
583 if (dt > 1.0 && dt < _opt->_corrWaitTime) {
584 return true;
585 }
586 else {
587 return false;
588 }
589 }
590 return false;
591}
Note: See TracBrowser for help on using the repository browser.