source: ntrip/branches/BNC_2.12/src/latencychecker.cpp@ 8012

Last change on this file since 8012 was 8012, checked in by stuerze, 8 years ago

check regarding wrong observation epochs is done in latencychecker as well to prevent erroneous latencies

File size: 16.2 KB
RevLine 
[1558]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: latencyChecker
30 *
31 * Purpose: Check incoming GNSS data for latencies, gaps etc.
32 *
33 * Author: G. Weber
34 *
35 * Created: 02-Feb-2009
36 *
37 * Changes:
38 *
39 * -----------------------------------------------------------------------*/
40
[1564]41#include <iostream>
[1578]42
[1577]43#ifdef WIN32
44#include <windows.h>
[1578]45#else
46#include <unistd.h>
[1577]47#endif
[1564]48
[1558]49#include "latencychecker.h"
[5070]50#include "bnccore.h"
[1558]51#include "bncutils.h"
52#include "bncsettings.h"
53
[1564]54using namespace std;
55
[1558]56// Constructor
57//////////////////////////////////////////////////////////////////////////////
58latencyChecker::latencyChecker(QByteArray staID) {
59
60 _staID = staID;
61
62 connect(this, SIGNAL(newMessage(QByteArray,bool)),
[5068]63 BNC_CORE, SLOT(slotMessage(const QByteArray,bool)));
[1558]64
65 bncSettings settings;
66
67 // Notice threshold
68 // ----------------
[7333]69 QString adviseObsRate = settings.value("adviseObsRate").toString();
[1558]70 _inspSegm = 0;
[7333]71 if ( adviseObsRate.isEmpty() ) {
[1558]72 _inspSegm = 0;
73 }
[7333]74 else if ( adviseObsRate.indexOf("5 Hz") != -1 ) {
[1558]75 _inspSegm = 20;
76 }
[7333]77 else if ( adviseObsRate.indexOf("1 Hz") != -1 ) {
[1558]78 _inspSegm = 10;
79 }
[7333]80 else if ( adviseObsRate.indexOf("0.5 Hz") != -1 ) {
[1558]81 _inspSegm = 20;
82 }
[7333]83 else if ( adviseObsRate.indexOf("0.2 Hz") != -1 ) {
[1558]84 _inspSegm = 40;
85 }
[7333]86 else if ( adviseObsRate.indexOf("0.1 Hz") != -1 ) {
[1558]87 _inspSegm = 50;
88 }
89 _adviseFail = settings.value("adviseFail").toInt();
90 _adviseReco = settings.value("adviseReco").toInt();
91 _adviseScript = settings.value("adviseScript").toString();
92 expandEnvVar(_adviseScript);
93
94 // Latency interval/average
95 // ------------------------
[7422]96 _miscIntr = 1;
97 QString miscIntr = settings.value("miscIntr").toString();
98 if ( miscIntr.isEmpty() ) {
99 _miscIntr = 1;
[1558]100 }
[7422]101 else if ( miscIntr.indexOf("2 sec") != -1 ) {
102 _miscIntr = 2;
[1558]103 }
[7422]104 else if ( miscIntr.indexOf("10 sec") != -1 ) {
105 _miscIntr = 10;
[1558]106 }
[7422]107 else if ( miscIntr.indexOf("1 min") != -1 ) {
108 _miscIntr = 60;
[1558]109 }
[7422]110 else if ( miscIntr.left(5).indexOf("5 min") != -1 ) {
111 _miscIntr = 300;
[1558]112 }
[7422]113 else if ( miscIntr.indexOf("15 min") != -1 ) {
114 _miscIntr = 900;
[1558]115 }
[7422]116 else if ( miscIntr.indexOf("1 hour") != -1 ) {
117 _miscIntr = 3600;
[1558]118 }
[7422]119 else if ( miscIntr.indexOf("6 hours") != -1 ) {
120 _miscIntr = 21600;
[1558]121 }
[7422]122 else if ( miscIntr.indexOf("1 day") != -1 ) {
123 _miscIntr = 86400;
[1558]124 }
125
126 // RTCM message types
127 // ------------------
128 _checkMountPoint = settings.value("miscMount").toString();
129
130 // Initialize private members
131 // --------------------------
[1568]132 _maxDt = 1000.0;
[1558]133 _wrongEpoch = false;
[1568]134 _checkSeg = false;
[1558]135 _numSucc = 0;
136 _secSucc = 0;
137 _secFail = 0;
[1568]138 _initPause = 0;
[1558]139 _currPause = 0;
140 _followSec = false;
141 _oldSecGPS = 0;
142 _newSecGPS = 0;
143 _numGaps = 0;
144 _diffSecGPS = 0;
145 _numLat = 0;
146 _sumLat = 0.0;
147 _sumLatQ = 0.0;
148 _meanDiff = 0.0;
149 _minLat = _maxDt;
150 _maxLat = -_maxDt;
151 _curLat = 0.0;
152
[1568]153 _checkTime = QDateTime::currentDateTime();
[6805]154 _decodeSucc = QDateTime::currentDateTime();
[6769]155
[6805]156 _decodeStop = QDateTime::currentDateTime();
157 _begDateTimeOut = QDateTime::currentDateTime();
158 _endDateTimeOut = QDateTime::currentDateTime();
[6820]159 _fromReconnect = false;
[1568]160
[6820]161 _decodeStopCorr = QDateTime::currentDateTime();
162 _begDateTimeCorr = QDateTime::currentDateTime();
163 _endDateTimeCorr = QDateTime::currentDateTime();
[6638]164
[1558]165}
166
167// Destructor
168//////////////////////////////////////////////////////////////////////////////
169latencyChecker::~latencyChecker() {
170}
171
[1568]172// Perform 'Begin outage' check
[1558]173//////////////////////////////////////////////////////////////////////////////
[1568]174void latencyChecker::checkReconnect() {
[1572]175
[6768]176 if (_inspSegm == 0) { return;}
[6758]177
[1572]178 // Begin outage threshold
179 // ----------------------
[6805]180 if (!_fromReconnect) {
181 _endDateTimeOut = QDateTime::currentDateTime();
182 }
183 _fromReconnect = true;
184
[1572]185 if ( _decodeStop.isValid() ) {
[6805]186 _begDateTimeOut = QDateTime::currentDateTime();
187 if ( _endDateTimeOut.secsTo(QDateTime::currentDateTime()) > _adviseFail * 60 ) {
188 _begDateOut = _endDateTimeOut.toUTC().date().toString("yy-MM-dd");
189 _begTimeOut = _endDateTimeOut.toUTC().time().toString("hh:mm:ss");
190 emit(newMessage((_staID + ": Failure threshold exceeded, outage since "
191 + _begDateOut + " " + _begTimeOut + " UTC").toAscii(), true));
[6807]192 callScript(("Begin_Outage "
[6805]193 + _begDateOut + " " + _begTimeOut + " UTC").toAscii());
[1572]194 _decodeStop.setDate(QDate());
195 _decodeStop.setTime(QTime());
[6805]196 _decodeStart = QDateTime::currentDateTime();
[1572]197 }
198 }
[1568]199}
200
201// Perform Corrupt and 'End outage' check
202//////////////////////////////////////////////////////////////////////////////
[1562]203void latencyChecker::checkOutage(bool decoded) {
[1558]204
[1568]205 if (_inspSegm == 0) { return;}
[1558]206
[1568]207 if (decoded) { _numSucc += 1; }
[1558]208
[1568]209 if (!_checkPause.isValid() || _checkPause.secsTo(QDateTime::currentDateTime()) >= _currPause ) {
210 if (!_checkSeg) {
211 if ( _checkTime.secsTo(QDateTime::currentDateTime()) > _inspSegm ) {
212 _checkSeg = true;
[1558]213 }
214 }
[1568]215
216 // Check - once per inspect segment
217 // --------------------------------
218 if (_checkSeg) {
219
220 _checkTime = QDateTime::currentDateTime();
221
222 if (_numSucc > 0) {
223 _secSucc += _inspSegm;
[6820]224 _secFail = 0;
[1568]225 _decodeSucc = QDateTime::currentDateTime();
226 if (_secSucc > _adviseReco * 60) {
227 _secSucc = _adviseReco * 60 + 1;
228 }
229 _numSucc = 0;
230 _currPause = _initPause;
231 _checkPause.setDate(QDate());
232 _checkPause.setTime(QTime());
[1558]233 }
234 else {
[1568]235 _secFail += _inspSegm;
236 _secSucc = 0;
[6805]237 if (_secFail > _adviseFail * 60) {
[1568]238 _secFail = _adviseFail * 60 + 1;
[1558]239 }
[1568]240 if (!_checkPause.isValid()) {
241 _checkPause = QDateTime::currentDateTime();
242 }
243 else {
244 _checkPause.setDate(QDate());
245 _checkPause.setTime(QTime());
246 _secFail = _secFail + _currPause - _inspSegm;
247 _currPause = _currPause * 2;
248 if (_currPause > 960) {
249 _currPause = 960;
250 }
251 }
[1558]252 }
[6805]253
[6820]254 // Begin corrupt threshold
255 // -----------------------
256 if (_secSucc > 0) {
257 _endDateTimeCorr = QDateTime::currentDateTime();
[6805]258 }
[1568]259
[6820]260 if (_secFail > 0) {
261 _begDateTimeCorr = QDateTime::currentDateTime();
262 }
263
264 if ( _decodeStopCorr.isValid() ) {
265 _begDateTimeCorr = QDateTime::currentDateTime();
266 if ( _endDateTimeCorr.secsTo(QDateTime::currentDateTime()) > _adviseFail * 60 ) {
267 _begDateCorr = _endDateTimeCorr.toUTC().date().toString("yy-MM-dd");
268 _begTimeCorr = _endDateTimeCorr.toUTC().time().toString("hh:mm:ss");
269 emit(newMessage((_staID + ": Failure threshold exceeded, corrupted since "
270 + _begDateCorr + " " + _begTimeCorr + " UTC").toAscii(), true));
[6805]271 callScript(("Begin_Corrupted "
[6820]272 + _begDateCorr + " " + _begTimeCorr + " UTC").toAscii());
[1568]273 _secSucc = 0;
274 _numSucc = 0;
[6820]275 _decodeStopCorr.setDate(QDate());
276 _decodeStopCorr.setTime(QTime());
277 _decodeStartCorr = QDateTime::currentDateTime();
[1568]278 }
[6820]279 }
280 else {
281
282 // End corrupt threshold
283 // ---------------------
284 if ( _decodeStartCorr.isValid() ) {
285 _endDateTimeCorr = QDateTime::currentDateTime();
286 if ( _begDateTimeCorr.secsTo(QDateTime::currentDateTime()) > _adviseReco * 60 ) {
287 _endDateCorr = _begDateTimeCorr.toUTC().date().toString("yy-MM-dd");
288 _endTimeCorr = _begDateTimeCorr.toUTC().time().toString("hh:mm:ss");
289 emit(newMessage((_staID + ": Recovery threshold exceeded, corruption ended "
290 + _endDateCorr + " " + _endTimeCorr + " UTC").toAscii(), true));
291 callScript(("End_Corrupted "
292 + _endDateCorr + " " + _endTimeCorr + " UTC Begin was "
293 + _begDateCorr + " " + _begTimeCorr + " UTC").toAscii());
294 _decodeStartCorr.setDate(QDate());
295 _decodeStartCorr.setTime(QTime());
296 _decodeStopCorr = QDateTime::currentDateTime();
297 _secFail = 0;
298 }
299 }
[1558]300 }
[1568]301 _checkSeg = false;
[1558]302 }
303 }
[1568]304
[6805]305 // End outage threshold
306 // --------------------
[6758]307 if (_fromReconnect) {
[6805]308 _begDateTimeOut = QDateTime::currentDateTime();
[6758]309 }
[6805]310 _fromReconnect = false;
[6758]311
[1568]312 if ( _decodeStart.isValid() ) {
[6805]313 _endDateTimeOut = QDateTime::currentDateTime();
314 if ( _begDateTimeOut.secsTo(QDateTime::currentDateTime()) > _adviseReco * 60 ) {
315 _endDateOut = _begDateTimeOut.toUTC().date().toString("yy-MM-dd");
316 _endTimeOut = _begDateTimeOut.toUTC().time().toString("hh:mm:ss");
317 emit(newMessage((_staID + ": Recovery threshold exceeded, outage ended "
318 + _endDateOut + " " + _endTimeOut + " UTC").toAscii(), true));
319 callScript(("End_Outage "
320 + _endDateOut + " " + _endTimeOut + " UTC Begin was "
321 + _begDateOut + " " + _begTimeOut + " UTC").toAscii());
[1568]322 _decodeStart.setDate(QDate());
323 _decodeStart.setTime(QTime());
[6805]324 _decodeStop = QDateTime::currentDateTime();
[1558]325 }
326 }
[1568]327}
328
[1562]329// Perform latency checks (observations)
330//////////////////////////////////////////////////////////////////////////////
[6137]331void latencyChecker::checkObsLatency(const QList<t_satObs>& obsList) {
[1562]332
[7422]333 if (_miscIntr > 0 ) {
[1558]334
[6137]335 QListIterator<t_satObs> it(obsList);
[1978]336 while (it.hasNext()) {
[6137]337 const t_satObs& obs = it.next();
[8012]338 bool wrongObservationEpoch = checkForWrongObsEpoch(obs._time);
[6137]339 _newSecGPS = static_cast<int>(obs._time.gpssec());
[8012]340 if (_newSecGPS != _oldSecGPS && !wrongObservationEpoch) {
[7422]341 if (_newSecGPS % _miscIntr < _oldSecGPS % _miscIntr) {
[1978]342 if (_numLat > 0) {
343 if (_meanDiff > 0.0) {
344 if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
[1558]345 emit( newMessage(QString("%1: Mean latency %2 sec, min %3, max %4, rms %5, %6 epochs, %7 gaps")
346 .arg(_staID.data())
347 .arg(int(_sumLat/_numLat*100)/100.)
348 .arg(int(_minLat*100)/100.)
349 .arg(int(_maxLat*100)/100.)
350 .arg(int((sqrt((_sumLatQ - _sumLat * _sumLat / _numLat)/_numLat))*100)/100.)
351 .arg(_numLat)
352 .arg(_numGaps)
353 .toAscii(), true) );
[1978]354 }
355 } else {
356 if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
[1558]357 emit( newMessage(QString("%1: Mean latency %2 sec, min %3, max %4, rms %5, %6 epochs")
358 .arg(_staID.data())
359 .arg(int(_sumLat/_numLat*100)/100.)
360 .arg(int(_minLat*100)/100.)
361 .arg(int(_maxLat*100)/100.)
362 .arg(int((sqrt((_sumLatQ - _sumLat * _sumLat / _numLat)/_numLat))*100)/100.)
363 .arg(_numLat)
364 .toAscii(), true) );
365 }
366 }
367 }
[1978]368 _meanDiff = _diffSecGPS / _numLat;
369 _diffSecGPS = 0;
370 _numGaps = 0;
371 _sumLat = 0.0;
372 _sumLatQ = 0.0;
373 _numLat = 0;
374 _minLat = _maxDt;
375 _maxLat = -_maxDt;
376 }
377 if (_followSec) {
378 _diffSecGPS += _newSecGPS - _oldSecGPS;
379 if (_meanDiff>0.) {
380 if (_newSecGPS - _oldSecGPS > 1.5 * _meanDiff) {
381 _numGaps += 1;
[1558]382 }
383 }
[1978]384 }
[1558]385
[1978]386 // Compute the observations latency
387 // --------------------------------
[6137]388 int week;
389 double sec;
[1978]390 currentGPSWeeks(week, sec);
391 const double secPerWeek = 7.0 * 24.0 * 3600.0;
[6137]392 if (week < int(obs._time.gpsw())) {
[1978]393 week += 1;
394 sec -= secPerWeek;
[1558]395 }
[6137]396 if (week > int(obs._time.gpsw())) {
[1978]397 week -= 1;
398 sec += secPerWeek;
399 }
[6137]400 _curLat = sec - obs._time.gpssec();
[1978]401 _sumLat += _curLat;
402 _sumLatQ += _curLat * _curLat;
403 if (_curLat < _minLat) {
404 _minLat = _curLat;
405 }
406 if (_curLat >= _maxLat) {
407 _maxLat = _curLat;
408 }
409 _numLat += 1;
410 _oldSecGPS = _newSecGPS;
411 _followSec = true;
[1558]412 }
413 }
414 }
415}
416
[1562]417// Perform latency checks (corrections)
418//////////////////////////////////////////////////////////////////////////////
[1566]419void latencyChecker::checkCorrLatency(int corrGPSEpochTime) {
[1562]420
[1566]421 if (corrGPSEpochTime < 0) {
[1562]422 return;
423 }
424
[7422]425 if (_miscIntr > 0) {
[1564]426
[1978]427 _newSecGPS = corrGPSEpochTime;
[1564]428
[1978]429 int week;
430 double sec;
431 currentGPSWeeks(week, sec);
432 double dt = fabs(sec - _newSecGPS);
433 const double secPerWeek = 7.0 * 24.0 * 3600.0;
434 if (dt > 0.5 * secPerWeek) {
435 if (sec > _newSecGPS) {
436 sec -= secPerWeek;
437 } else {
438 sec += secPerWeek;
[1566]439 }
[1978]440 }
441 if (_newSecGPS != _oldSecGPS) {
[7422]442 if (int(_newSecGPS) % _miscIntr < int(_oldSecGPS) % _miscIntr) {
[1978]443 if (_numLat>0) {
444 QString late;
445 if (_meanDiff>0.) {
446 late = QString(": Mean latency %1 sec, min %2, max %3, rms %4, %5 epochs, %6 gaps")
447 .arg(int(_sumLat/_numLat*100)/100.)
448 .arg(int(_minLat*100)/100.)
449 .arg(int(_maxLat*100)/100.)
450 .arg(int((sqrt((_sumLatQ - _sumLat * _sumLat / _numLat)/_numLat))*100)/100.)
451 .arg(_numLat)
452 .arg(_numGaps);
453 if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
[1566]454 emit(newMessage(QString(_staID + late ).toAscii(), true) );
[1562]455 }
[1978]456 }
457 else {
458 late = QString(": Mean latency %1 sec, min %2, max %3, rms %4, %5 epochs")
459 .arg(int(_sumLat/_numLat*100)/100.)
460 .arg(int(_minLat*100)/100.)
461 .arg(int(_maxLat*100)/100.)
462 .arg(int((sqrt((_sumLatQ - _sumLat * _sumLat / _numLat)/_numLat))*100)/100.)
463 .arg(_numLat);
464 if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
465 emit(newMessage(QString(_staID + late ).toAscii(), true) );
466 }
[1564]467 }
[1566]468 }
[1978]469 _meanDiff = int(_diffSecGPS)/_numLat;
470 _diffSecGPS = 0;
471 _numGaps = 0;
472 _sumLat = 0.0;
473 _sumLatQ = 0.0;
474 _numLat = 0;
475 _minLat = 1000.;
476 _maxLat = -1000.;
477 }
478 if (_followSec) {
479 _diffSecGPS += _newSecGPS - _oldSecGPS;
480 if (_meanDiff>0.) {
481 if (_newSecGPS - _oldSecGPS > 1.5 * _meanDiff) {
482 _numGaps += 1;
[1562]483 }
484 }
485 }
[1978]486 _curLat = sec - _newSecGPS;
487 _sumLat += _curLat;
488 _sumLatQ += _curLat * _curLat;
489 if (_curLat < _minLat) {
490 _minLat = _curLat;
491 }
492 if (_curLat >= _maxLat) {
493 _maxLat = _curLat;
494 }
495 _numLat += 1;
496 _oldSecGPS = _newSecGPS;
497 _followSec = true;
[1562]498 }
499 }
500}
501
[1558]502// Call advisory notice script
503////////////////////////////////////////////////////////////////////////////
504void latencyChecker::callScript(const char* comment) {
505 if (!_adviseScript.isEmpty()) {
506#ifdef WIN32
[1577]507 Sleep(1);
[1558]508 QProcess::startDetached(_adviseScript, QStringList() << _staID << comment) ;
509#else
[1577]510 sleep(1);
[1558]511 QProcess::startDetached("nohup", QStringList() << _adviseScript << _staID << comment) ;
512#endif
513 }
514}
Note: See TracBrowser for help on using the repository browser.