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

Last change on this file since 7107 was 7055, checked in by stuerze, 10 years ago

IOD data type changed, to support IODs computed from CRC over broadcasted ephemris and clock parameters

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