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

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