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

Last change on this file since 8012 was 8012, checked in by stuerze, 3 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.