source: ntrip/trunk/BNC/latencychecker.cpp@ 1835

Last change on this file since 1835 was 1578, checked in by weber, 16 years ago

* empty log message *

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