source: ntrip/trunk/BNC/src/latencychecker.cpp@ 6769

Last change on this file since 6769 was 6769, checked in by weber, 7 years ago

Failure/outage procedure modified

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