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

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

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

File size: 16.2 KB
Line 
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
41#include <iostream>
42
43#ifdef WIN32
44#include <windows.h>
45#else
46#include <unistd.h>
47#endif
48
49#include "latencychecker.h"
50#include "bnccore.h"
51#include "bncutils.h"
52#include "bncsettings.h"
53
54using namespace std;
55
56// Constructor
57//////////////////////////////////////////////////////////////////////////////
58latencyChecker::latencyChecker(QByteArray staID) {
59
60 _staID = staID;
61
62 connect(this, SIGNAL(newMessage(QByteArray,bool)),
63 BNC_CORE, SLOT(slotMessage(const QByteArray,bool)));
64
65 bncSettings settings;
66
67 // Notice threshold
68 // ----------------
69 QString adviseObsRate = settings.value("adviseObsRate").toString();
70 _inspSegm = 0;
71 if ( adviseObsRate.isEmpty() ) {
72 _inspSegm = 0;
73 }
74 else if ( adviseObsRate.indexOf("5 Hz") != -1 ) {
75 _inspSegm = 20;
76 }
77 else if ( adviseObsRate.indexOf("1 Hz") != -1 ) {
78 _inspSegm = 10;
79 }
80 else if ( adviseObsRate.indexOf("0.5 Hz") != -1 ) {
81 _inspSegm = 20;
82 }
83 else if ( adviseObsRate.indexOf("0.2 Hz") != -1 ) {
84 _inspSegm = 40;
85 }
86 else if ( adviseObsRate.indexOf("0.1 Hz") != -1 ) {
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 // ------------------------
96 _miscIntr = 1;
97 QString miscIntr = settings.value("miscIntr").toString();
98 if ( miscIntr.isEmpty() ) {
99 _miscIntr = 1;
100 }
101 else if ( miscIntr.indexOf("2 sec") != -1 ) {
102 _miscIntr = 2;
103 }
104 else if ( miscIntr.indexOf("10 sec") != -1 ) {
105 _miscIntr = 10;
106 }
107 else if ( miscIntr.indexOf("1 min") != -1 ) {
108 _miscIntr = 60;
109 }
110 else if ( miscIntr.left(5).indexOf("5 min") != -1 ) {
111 _miscIntr = 300;
112 }
113 else if ( miscIntr.indexOf("15 min") != -1 ) {
114 _miscIntr = 900;
115 }
116 else if ( miscIntr.indexOf("1 hour") != -1 ) {
117 _miscIntr = 3600;
118 }
119 else if ( miscIntr.indexOf("6 hours") != -1 ) {
120 _miscIntr = 21600;
121 }
122 else if ( miscIntr.indexOf("1 day") != -1 ) {
123 _miscIntr = 86400;
124 }
125
126 // RTCM message types
127 // ------------------
128 _checkMountPoint = settings.value("miscMount").toString();
129
130 // Initialize private members
131 // --------------------------
132 _maxDt = 1000.0;
133 _wrongEpoch = false;
134 _checkSeg = false;
135 _numSucc = 0;
136 _secSucc = 0;
137 _secFail = 0;
138 _initPause = 0;
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
153 _checkTime = QDateTime::currentDateTime();
154 _decodeSucc = QDateTime::currentDateTime();
155
156 _decodeStop = QDateTime::currentDateTime();
157 _begDateTimeOut = QDateTime::currentDateTime();
158 _endDateTimeOut = QDateTime::currentDateTime();
159 _fromReconnect = false;
160
161 _decodeStopCorr = QDateTime::currentDateTime();
162 _begDateTimeCorr = QDateTime::currentDateTime();
163 _endDateTimeCorr = QDateTime::currentDateTime();
164
165}
166
167// Destructor
168//////////////////////////////////////////////////////////////////////////////
169latencyChecker::~latencyChecker() {
170}
171
172// Perform 'Begin outage' check
173//////////////////////////////////////////////////////////////////////////////
174void latencyChecker::checkReconnect() {
175
176 if (_inspSegm == 0) { return;}
177
178 // Begin outage threshold
179 // ----------------------
180 if (!_fromReconnect) {
181 _endDateTimeOut = QDateTime::currentDateTime();
182 }
183 _fromReconnect = true;
184
185 if ( _decodeStop.isValid() ) {
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));
192 callScript(("Begin_Outage "
193 + _begDateOut + " " + _begTimeOut + " UTC").toAscii());
194 _decodeStop.setDate(QDate());
195 _decodeStop.setTime(QTime());
196 _decodeStart = QDateTime::currentDateTime();
197 }
198 }
199}
200
201// Perform Corrupt and 'End outage' check
202//////////////////////////////////////////////////////////////////////////////
203void latencyChecker::checkOutage(bool decoded) {
204
205 if (_inspSegm == 0) { return;}
206
207 if (decoded) { _numSucc += 1; }
208
209 if (!_checkPause.isValid() || _checkPause.secsTo(QDateTime::currentDateTime()) >= _currPause ) {
210 if (!_checkSeg) {
211 if ( _checkTime.secsTo(QDateTime::currentDateTime()) > _inspSegm ) {
212 _checkSeg = true;
213 }
214 }
215
216 // Check - once per inspect segment
217 // --------------------------------
218 if (_checkSeg) {
219
220 _checkTime = QDateTime::currentDateTime();
221
222 if (_numSucc > 0) {
223 _secSucc += _inspSegm;
224 _secFail = 0;
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());
233 }
234 else {
235 _secFail += _inspSegm;
236 _secSucc = 0;
237 if (_secFail > _adviseFail * 60) {
238 _secFail = _adviseFail * 60 + 1;
239 }
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 }
252 }
253
254 // Begin corrupt threshold
255 // -----------------------
256 if (_secSucc > 0) {
257 _endDateTimeCorr = QDateTime::currentDateTime();
258 }
259
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));
271 callScript(("Begin_Corrupted "
272 + _begDateCorr + " " + _begTimeCorr + " UTC").toAscii());
273 _secSucc = 0;
274 _numSucc = 0;
275 _decodeStopCorr.setDate(QDate());
276 _decodeStopCorr.setTime(QTime());
277 _decodeStartCorr = QDateTime::currentDateTime();
278 }
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 }
300 }
301 _checkSeg = false;
302 }
303 }
304
305 // End outage threshold
306 // --------------------
307 if (_fromReconnect) {
308 _begDateTimeOut = QDateTime::currentDateTime();
309 }
310 _fromReconnect = false;
311
312 if ( _decodeStart.isValid() ) {
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());
322 _decodeStart.setDate(QDate());
323 _decodeStart.setTime(QTime());
324 _decodeStop = QDateTime::currentDateTime();
325 }
326 }
327}
328
329// Perform latency checks (observations)
330//////////////////////////////////////////////////////////////////////////////
331void latencyChecker::checkObsLatency(const QList<t_satObs>& obsList) {
332
333 if (_miscIntr > 0 ) {
334
335 QListIterator<t_satObs> it(obsList);
336 while (it.hasNext()) {
337 const t_satObs& obs = it.next();
338 bool wrongObservationEpoch = checkForWrongObsEpoch(obs._time);
339 _newSecGPS = static_cast<int>(obs._time.gpssec());
340 if (_newSecGPS != _oldSecGPS && !wrongObservationEpoch) {
341 if (_newSecGPS % _miscIntr < _oldSecGPS % _miscIntr) {
342 if (_numLat > 0) {
343 if (_meanDiff > 0.0) {
344 if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
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) );
354 }
355 } else {
356 if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
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 }
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;
382 }
383 }
384 }
385
386 // Compute the observations latency
387 // --------------------------------
388 int week;
389 double sec;
390 currentGPSWeeks(week, sec);
391 const double secPerWeek = 7.0 * 24.0 * 3600.0;
392 if (week < int(obs._time.gpsw())) {
393 week += 1;
394 sec -= secPerWeek;
395 }
396 if (week > int(obs._time.gpsw())) {
397 week -= 1;
398 sec += secPerWeek;
399 }
400 _curLat = sec - obs._time.gpssec();
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;
412 }
413 }
414 }
415}
416
417// Perform latency checks (corrections)
418//////////////////////////////////////////////////////////////////////////////
419void latencyChecker::checkCorrLatency(int corrGPSEpochTime) {
420
421 if (corrGPSEpochTime < 0) {
422 return;
423 }
424
425 if (_miscIntr > 0) {
426
427 _newSecGPS = corrGPSEpochTime;
428
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;
439 }
440 }
441 if (_newSecGPS != _oldSecGPS) {
442 if (int(_newSecGPS) % _miscIntr < int(_oldSecGPS) % _miscIntr) {
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" ) {
454 emit(newMessage(QString(_staID + late ).toAscii(), true) );
455 }
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 }
467 }
468 }
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;
483 }
484 }
485 }
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;
498 }
499 }
500}
501
502// Call advisory notice script
503////////////////////////////////////////////////////////////////////////////
504void latencyChecker::callScript(const char* comment) {
505 if (!_adviseScript.isEmpty()) {
506#ifdef WIN32
507 Sleep(1);
508 QProcess::startDetached(_adviseScript, QStringList() << _staID << comment) ;
509#else
510 sleep(1);
511 QProcess::startDetached("nohup", QStringList() << _adviseScript << _staID << comment) ;
512#endif
513 }
514}
Note: See TracBrowser for help on using the repository browser.