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

Last change on this file since 6816 was 6653, checked in by stuerze, 10 years ago

sinex tro file support added,
troposphere results in overall ppp logfile added

File size: 16.3 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(newOrbCorrections(QList<t_orbCorr>)),
101 this, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)),conType);
102
103 connect(BNC_CORE, SIGNAL(newClkCorrections(QList<t_clkCorr>)),
104 this, SLOT(slotNewClkCorrections(QList<t_clkCorr>)),conType);
105
106 connect(BNC_CORE, SIGNAL(newCodeBiases(QList<t_satCodeBias>)),
107 this, SLOT(slotNewCodeBiases(QList<t_satCodeBias>)),conType);
108 }
109 else {
110 _rnxObsFile = 0;
111 _rnxNavFile = 0;
112 _corrFile = 0;
113 _speed = settings.value("PPP/mapSpeedSlider").toInt();
114 connect(this, SIGNAL(progressRnxPPP(int)), BNC_CORE, SIGNAL(progressRnxPPP(int)));
115 connect(this, SIGNAL(finishedRnxPPP()), BNC_CORE, SIGNAL(finishedRnxPPP()));
116 connect(BNC_CORE, SIGNAL(mapSpeedSliderChanged(int)),
117 this, SLOT(slotSetSpeed(int)));
118 connect(BNC_CORE, SIGNAL(stopRinexPPP()), this, SLOT(slotSetStopFlag()));
119 }
120
121 _stopFlag = false;
122
123 QString roverName(_opt->_roverName.c_str());
124
125 QString logFileSkl = settings.value("PPP/logFile").toString();
126 if (logFileSkl.isEmpty()) {
127 _logFile = 0;
128 }
129 else {
130 if (logFileSkl.indexOf("${STATION}") == -1) {
131 logFileSkl = logFileSkl + "_" + roverName;
132 }
133 else {
134 logFileSkl.replace("${STATION}", roverName);
135 }
136 _logFile = new bncoutf(logFileSkl, "1 day", 0);
137 }
138
139 QString nmeaFileSkl = settings.value("PPP/nmeaFile").toString();
140 if (nmeaFileSkl.isEmpty()) {
141 _nmeaFile = 0;
142 }
143 else {
144 if (nmeaFileSkl.indexOf("${STATION}") == -1) {
145 nmeaFileSkl = roverName + "_" + nmeaFileSkl;
146 }
147 else {
148 nmeaFileSkl.replace("${STATION}", roverName);
149 }
150 _nmeaFile = new bncoutf(nmeaFileSkl, "1 day", 0);
151 }
152
153 QString snxtroFileSkl = settings.value("PPP/snxtroFile").toString();
154 if (snxtroFileSkl.isEmpty()) {
155 _snxtroFile = 0;
156 }
157 else {
158 if (snxtroFileSkl.indexOf("${STATION}") == -1) {
159 snxtroFileSkl = snxtroFileSkl + "_" + roverName;
160 }
161 else {
162 snxtroFileSkl.replace("${STATION}", roverName.left(4));
163 }
164 int samplSnxTro = settings.value("PPP/snxtroSampl").toInt();
165 _snxtroFile = new bncSinexTro(_opt, snxtroFileSkl, "1 day", samplSnxTro);
166 }
167}
168
169
170
171// Destructor
172////////////////////////////////////////////////////////////////////////////
173t_pppRun::~t_pppRun() {
174 delete _logFile;
175 delete _nmeaFile;
176 delete _snxtroFile;
177}
178
179//
180////////////////////////////////////////////////////////////////////////////
181void t_pppRun::slotNewGPSEph(t_ephGPS eph) {
182 QMutexLocker locker(&_mutex);
183 _pppClient->putEphemeris(&eph);
184}
185
186//
187////////////////////////////////////////////////////////////////////////////
188void t_pppRun::slotNewGlonassEph(t_ephGlo eph) {
189 QMutexLocker locker(&_mutex);
190 _pppClient->putEphemeris(&eph);
191}
192
193//
194////////////////////////////////////////////////////////////////////////////
195void t_pppRun::slotNewGalileoEph(t_ephGal eph) {
196 QMutexLocker locker(&_mutex);
197 _pppClient->putEphemeris(&eph);
198}
199
200//
201////////////////////////////////////////////////////////////////////////////
202void t_pppRun::slotNewObs(QByteArray staID, QList<t_satObs> obsList) {
203 QMutexLocker locker(&_mutex);
204
205 if (string(staID.data()) != _opt->_roverName) {
206 return;
207 }
208
209 // Loop over all obsevations (possible different epochs)
210 // -----------------------------------------------------
211 QListIterator<t_satObs> it(obsList);
212 while (it.hasNext()) {
213 const t_satObs& oldObs = it.next();
214 t_satObs* newObs = new t_satObs(oldObs);
215
216 // Find the corresponding data epoch or create a new one
217 // -----------------------------------------------------
218 t_epoData* epoch = 0;
219 deque<t_epoData*>::const_iterator it;
220 for (it = _epoData.begin(); it != _epoData.end(); it++) {
221 if (newObs->_time == (*it)->_time) {
222 epoch = *it;
223 break;
224 }
225 }
226 if (epoch == 0) {
227 if (_epoData.empty() || newObs->_time > _epoData.back()->_time) {
228 epoch = new t_epoData;
229 epoch->_time = newObs->_time;
230 _epoData.push_back(epoch);
231 }
232 }
233
234 // Put data into the epoch
235 // -----------------------
236 if (epoch != 0) {
237 epoch->_satObs.push_back(newObs);
238 }
239 else {
240 delete newObs;
241 }
242 }
243
244 // Process the oldest epochs
245 // ------------------------
246 while (_epoData.size() && !waitForCorr(_epoData.front()->_time)) {
247
248 const vector<t_satObs*>& satObs = _epoData.front()->_satObs;
249
250 t_output output;
251 _pppClient->processEpoch(satObs, &output);
252
253 if (!output._error) {
254 QVector<double> xx(6);
255 xx.data()[0] = output._xyzRover[0];
256 xx.data()[1] = output._xyzRover[1];
257 xx.data()[2] = output._xyzRover[2];
258 xx.data()[3] = output._neu[0];
259 xx.data()[4] = output._neu[1];
260 xx.data()[5] = output._neu[2];
261 emit newPosition(staID, output._epoTime, xx);
262 }
263
264 delete _epoData.front(); _epoData.pop_front();
265
266 ostringstream log;
267 if (output._error) {
268 log << output._log;
269 }
270 else {
271 log.setf(ios::fixed);
272 log << string(output._epoTime) << ' ' << staID.data()
273 << " X = " << setprecision(4) << output._xyzRover[0]
274 << " Y = " << setprecision(4) << output._xyzRover[1]
275 << " Z = " << setprecision(4) << output._xyzRover[2]
276 << " NEU: " << showpos << setw(8) << setprecision(4) << output._neu[0]
277 << " " << showpos << setw(8) << setprecision(4) << output._neu[1]
278 << " " << showpos << setw(8) << setprecision(4) << output._neu[2]
279 << " TRP: " << showpos << setw(8) << setprecision(4) << output._trp0
280 << " " << showpos << setw(8) << setprecision(4) << output._trp;
281 }
282
283 if (_logFile && output._epoTime.valid()) {
284 _logFile->write(output._epoTime.gpsw(), output._epoTime.gpssec(),
285 QString(output._log.c_str()));
286 }
287
288 if (!output._error) {
289 QString rmcStr = nmeaString('R', output);
290 QString ggaStr = nmeaString('G', output);
291 if (_nmeaFile) {
292 _nmeaFile->write(output._epoTime.gpsw(), output._epoTime.gpssec(), rmcStr);
293 _nmeaFile->write(output._epoTime.gpsw(), output._epoTime.gpssec(), ggaStr);
294 }
295 emit newNMEAstr(staID, rmcStr.toAscii());
296 emit newNMEAstr(staID, ggaStr.toAscii());
297 }
298
299 if (_snxtroFile && output._epoTime.valid()) {
300 _snxtroFile->write(staID, int(output._epoTime.gpsw()), output._epoTime.gpssec(),
301 output._trp0 + output._trp, output._trpStdev);
302 }
303
304 emit newMessage(QByteArray(log.str().c_str()), true);
305 }
306}
307
308//
309////////////////////////////////////////////////////////////////////////////
310void t_pppRun::slotNewOrbCorrections(QList<t_orbCorr> orbCorr) {
311 if (orbCorr.size() == 0) {
312 return;
313 }
314
315 if (_opt->_realTime) {
316 if (_opt->_corrMount.empty() || _opt->_corrMount != orbCorr[0]._staID) {
317 return;
318 }
319 }
320 vector<t_orbCorr*> corrections;
321 for (int ii = 0; ii < orbCorr.size(); ii++) {
322 corrections.push_back(new t_orbCorr(orbCorr[ii]));
323 _lastClkCorrTime = orbCorr[ii]._time;
324 }
325
326 _pppClient->putOrbCorrections(corrections);
327}
328
329//
330////////////////////////////////////////////////////////////////////////////
331void t_pppRun::slotNewClkCorrections(QList<t_clkCorr> clkCorr) {
332 if (clkCorr.size() == 0) {
333 return;
334 }
335
336 if (_opt->_realTime) {
337 if (_opt->_corrMount.empty() || _opt->_corrMount != clkCorr[0]._staID) {
338 return;
339 }
340 }
341 vector<t_clkCorr*> corrections;
342 for (int ii = 0; ii < clkCorr.size(); ii++) {
343 corrections.push_back(new t_clkCorr(clkCorr[ii]));
344 }
345
346 _pppClient->putClkCorrections(corrections);
347}
348
349//
350////////////////////////////////////////////////////////////////////////////
351void t_pppRun::slotNewCodeBiases(QList<t_satCodeBias> codeBiases) {
352 if (codeBiases.size() == 0) {
353 return;
354 }
355
356 if (_opt->_realTime) {
357 if (_opt->_corrMount.empty() || _opt->_corrMount != codeBiases[0]._staID) {
358 return;
359 }
360 }
361 vector<t_satCodeBias*> biases;
362 for (int ii = 0; ii < codeBiases.size(); ii++) {
363 biases.push_back(new t_satCodeBias(codeBiases[ii]));
364 }
365
366 _pppClient->putCodeBiases(biases);
367}
368
369//
370////////////////////////////////////////////////////////////////////////////
371void t_pppRun::processFiles() {
372
373 try {
374 _rnxObsFile = new t_rnxObsFile(QString(_opt->_rinexObs.c_str()), t_rnxObsFile::input);
375 }
376 catch (...) {
377 delete _rnxObsFile; _rnxObsFile = 0;
378 emit finishedRnxPPP();
379 return;
380 }
381
382 _rnxNavFile = new t_rnxNavFile(QString(_opt->_rinexNav.c_str()), t_rnxNavFile::input);
383
384 if (!_opt->_corrFile.empty()) {
385 _corrFile = new t_corrFile(QString(_opt->_corrFile.c_str()));
386 connect(_corrFile, SIGNAL(newOrbCorrections(QList<t_orbCorr>)),
387 this, SLOT(slotNewOrbCorrections(QList<t_orbCorr>)));
388 connect(_corrFile, SIGNAL(newClkCorrections(QList<t_clkCorr>)),
389 this, SLOT(slotNewClkCorrections(QList<t_clkCorr>)));
390 connect(_corrFile, SIGNAL(newCodeBiases(QList<t_satCodeBias>)),
391 this, SLOT(slotNewCodeBiases(QList<t_satCodeBias>)));
392 }
393
394 // Read/Process Observations
395 // -------------------------
396 int nEpo = 0;
397 const t_rnxObsFile::t_rnxEpo* epo = 0;
398 while ( !_stopFlag && (epo = _rnxObsFile->nextEpoch()) != 0 ) {
399 ++nEpo;
400
401 if (_speed < 100) {
402 double sleepTime = 2.0 / _speed;
403 t_pppThread::msleep(int(sleepTime*1.e3));
404 }
405
406 // Get Corrections
407 // ---------------
408 if (_corrFile) {
409 try {
410 _corrFile->syncRead(epo->tt);
411 }
412 catch (const char* msg) {
413 emit newMessage(QByteArray(msg), true);
414 break;
415 }
416 catch (const string& msg) {
417 emit newMessage(QByteArray(msg.c_str()), true);
418 break;
419 }
420 catch (...) {
421 emit newMessage("unknown exceptions in corrFile", true);
422 break;
423 }
424 }
425
426 // Get Ephemerides
427 // ----------------
428 t_eph* eph = 0;
429 const QMap<QString, int>* corrIODs = _corrFile ? &_corrFile->corrIODs() : 0;
430 while ( (eph = _rnxNavFile->getNextEph(epo->tt, corrIODs)) != 0 ) {
431 _pppClient->putEphemeris(eph);
432 delete eph; eph = 0;
433 }
434
435 // Create list of observations and start epoch processing
436 // ------------------------------------------------------
437 QList<t_satObs> obsList;
438 for (unsigned iObs = 0; iObs < epo->rnxSat.size(); iObs++) {
439 const t_rnxObsFile::t_rnxSat& rnxSat = epo->rnxSat[iObs];
440
441 t_satObs obs;
442 t_rnxObsFile::setObsFromRnx(_rnxObsFile, epo, rnxSat, obs);
443 obsList << obs;
444 }
445 slotNewObs(QByteArray(_opt->_roverName.c_str()), obsList);
446
447
448 if (nEpo % 10 == 0) {
449 emit progressRnxPPP(nEpo);
450 }
451
452 QCoreApplication::processEvents();
453 }
454
455 emit finishedRnxPPP();
456
457 if (BNC_CORE->mode() != t_bncCore::interactive) {
458 qApp->exit(0);
459 }
460 else {
461 BNC_CORE->stopPPP();
462 }
463}
464
465//
466////////////////////////////////////////////////////////////////////////////
467void t_pppRun::slotSetSpeed(int speed) {
468 QMutexLocker locker(&_mutex);
469 _speed = speed;
470}
471
472//
473////////////////////////////////////////////////////////////////////////////
474void t_pppRun::slotSetStopFlag() {
475 QMutexLocker locker(&_mutex);
476 _stopFlag = true;
477}
478
479//
480////////////////////////////////////////////////////////////////////////////
481QString t_pppRun::nmeaString(char strType, const t_output& output) {
482
483 double ell[3];
484 xyz2ell(output._xyzRover, ell);
485 double phiDeg = ell[0] * 180 / M_PI;
486 double lamDeg = ell[1] * 180 / M_PI;
487
488 char phiCh = 'N';
489 if (phiDeg < 0) {
490 phiDeg = -phiDeg;
491 phiCh = 'S';
492 }
493 char lamCh = 'E';
494 if (lamDeg < 0) {
495 lamDeg = -lamDeg;
496 lamCh = 'W';
497 }
498
499 ostringstream out;
500 out.setf(ios::fixed);
501
502 if (strType == 'R') {
503 string datestr = output._epoTime.datestr(0); // yyyymmdd
504 out << "GPRMC,"
505 << output._epoTime.timestr(0,0) << ",A,"
506 << setw(2) << setfill('0') << int(phiDeg)
507 << setw(6) << setprecision(3) << setfill('0')
508 << fmod(60*phiDeg,60) << ',' << phiCh << ','
509 << setw(3) << setfill('0') << int(lamDeg)
510 << setw(6) << setprecision(3) << setfill('0')
511 << fmod(60*lamDeg,60) << ',' << lamCh << ",,,"
512 << datestr[6] << datestr[7] << datestr[4] << datestr[5]
513 << datestr[2] << datestr[3] << ",,";
514 }
515 else if (strType == 'G') {
516 out << "GPGGA,"
517 << output._epoTime.timestr(0,0) << ','
518 << setw(2) << setfill('0') << int(phiDeg)
519 << setw(10) << setprecision(7) << setfill('0')
520 << fmod(60*phiDeg,60) << ',' << phiCh << ','
521 << setw(3) << setfill('0') << int(lamDeg)
522 << setw(10) << setprecision(7) << setfill('0')
523 << fmod(60*lamDeg,60) << ',' << lamCh
524 << ",1," << setw(2) << setfill('0') << output._numSat << ','
525 << setw(3) << setprecision(1) << output._pDop << ','
526 << setprecision(3) << ell[2] << ",M,0.0,M,,";
527 }
528 else {
529 return "";
530 }
531
532 QString nmStr(out.str().c_str());
533 unsigned char XOR = 0;
534 for (int ii = 0; ii < nmStr.length(); ii++) {
535 XOR ^= (unsigned char) nmStr[ii].toAscii();
536 }
537
538 return '$' + nmStr + QString("*%1\n").arg(int(XOR), 0, 16).toUpper();
539}
540
541//
542////////////////////////////////////////////////////////////////////////////
543bool t_pppRun::waitForCorr(const bncTime& epoTime) const {
544
545 if (!_opt->_realTime || _opt->_corrMount.empty()) {
546 return false;
547 }
548 else if (!_lastClkCorrTime.valid()) {
549 return true;
550 }
551 else {
552 double dt = epoTime - _lastClkCorrTime;
553 if (dt > 1.0 && dt < _opt->_corrWaitTime) {
554 return true;
555 }
556 else {
557 return false;
558 }
559 }
560 return false;
561}
Note: See TracBrowser for help on using the repository browser.