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

Last change on this file since 1577 was 1577, checked in by weber, 15 years ago

* empty log message *

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