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

Last change on this file since 8673 was 8673, checked in by stuerze, 15 months ago

check with respect to wrong observation epochs is improved

File size: 18.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  _wrongEpoch = false;
133  _checkSeg   = false;
134  _numSucc    = 0;
135  _secSucc    = 0;
136  _secFail    = 0;
137  _initPause  = 0;
138  _currPause  = 0;
139  _endCorrupt = false;
140  _begCorrupt = false;
141  _fromCorrupt = false;
142
143  _checkTime = QDateTime::currentDateTime();
144  _decodeSucc = QDateTime::currentDateTime();
145
146  _decodeStop = QDateTime::currentDateTime();
147  _begDateTimeOut = QDateTime::currentDateTime();
148  _endDateTimeOut = QDateTime::currentDateTime();
149  _fromReconnect = false;
150
151  _decodeStopCorr = QDateTime::currentDateTime();
152  _begDateTimeCorr = QDateTime::currentDateTime();
153  _endDateTimeCorr = QDateTime::currentDateTime();
154}
155
156// Destructor
157//////////////////////////////////////////////////////////////////////////////
158latencyChecker::~latencyChecker() {
159}
160
161// Perform 'Begin outage' check
162//////////////////////////////////////////////////////////////////////////////
163void latencyChecker::checkReconnect() {
164
165  if (_inspSegm == 0) { return;}
166
167  // Begin outage threshold
168  // ----------------------
169  if (!_fromReconnect) {
170    _endDateTimeOut = QDateTime::currentDateTime();
171  }
172  _fromReconnect = true;
173
174  if ( _decodeStop.isValid() ) {
175    _begDateTimeOut = QDateTime::currentDateTime();
176    if ( _endDateTimeOut.secsTo(QDateTime::currentDateTime()) >  _adviseFail * 60 ) {
177      _begDateOut = _endDateTimeOut.toUTC().date().toString("yy-MM-dd");
178      _begTimeOut = _endDateTimeOut.toUTC().time().toString("hh:mm:ss");
179      emit(newMessage((_staID + ": Failure threshold exceeded, outage since "
180                    + _begDateOut + " " + _begTimeOut + " UTC").toLatin1(), true));
181      callScript(("Begin_Outage "
182                    + _begDateOut + " " + _begTimeOut + " UTC").toLatin1());
183      _decodeStop.setDate(QDate());
184      _decodeStop.setTime(QTime());
185      _decodeStart = QDateTime::currentDateTime();
186    }
187  }
188}
189
190// Perform Corrupt and 'End outage' check
191//////////////////////////////////////////////////////////////////////////////
192void latencyChecker::checkOutage(bool decoded) {
193
194  if (_inspSegm == 0) { return;}
195
196  if (decoded) { _numSucc += 1; }
197
198  if (!_checkPause.isValid() || _checkPause.secsTo(QDateTime::currentDateTime()) >= _currPause )  {
199    if (!_checkSeg) {
200      if ( _checkTime.secsTo(QDateTime::currentDateTime()) > _inspSegm ) {
201        _checkSeg = true;
202      }
203    }
204
205    // Check - once per inspect segment
206    // --------------------------------
207    if (_checkSeg) {
208
209      _checkTime = QDateTime::currentDateTime();
210
211      if (_numSucc > 0) {
212        _secSucc += _inspSegm;
213        _secFail = 0;
214        _decodeSucc = QDateTime::currentDateTime();
215        if (_secSucc > _adviseReco * 60) {
216          _secSucc = _adviseReco * 60 + 1;
217        }
218        _numSucc = 0;
219        _currPause = _initPause;
220        _checkPause.setDate(QDate());
221        _checkPause.setTime(QTime());
222      }
223      else {
224        _secFail += _inspSegm;
225        _secSucc = 0;
226        if (_secFail > _adviseFail * 60) {
227          _secFail = _adviseFail * 60 + 1;
228        }
229        if (!_checkPause.isValid()) {
230          _checkPause = QDateTime::currentDateTime();
231        }
232        else {
233          _checkPause.setDate(QDate());
234          _checkPause.setTime(QTime());
235          _secFail = _secFail + _currPause - _inspSegm;
236          _currPause = _currPause * 2;
237          if (_currPause > 960) {
238            _currPause = 960;
239          }
240        }
241      }
242
243      // Begin corrupt threshold
244      // -----------------------
245      if (_secSucc > 0) {
246        _endDateTimeCorr = QDateTime::currentDateTime();
247      }
248
249      if (_secFail > 0) {
250        _begDateTimeCorr = QDateTime::currentDateTime();
251      }
252
253      if ( _decodeStopCorr.isValid() ) {
254        _begDateTimeCorr = QDateTime::currentDateTime();
255        if ( _endDateTimeCorr.secsTo(QDateTime::currentDateTime()) > _adviseFail * 60 ) {
256          _begDateCorr = _endDateTimeCorr.toUTC().date().toString("yy-MM-dd");
257          _begTimeCorr = _endDateTimeCorr.toUTC().time().toString("hh:mm:ss");
258          emit(newMessage((_staID + ": Failure threshold exceeded, corrupted since "
259                    + _begDateCorr + " " + _begTimeCorr + " UTC").toLatin1(), true));
260          callScript(("Begin_Corrupted "
261                    + _begDateCorr + " " + _begTimeCorr + " UTC").toLatin1());
262          _secSucc = 0;
263          _numSucc = 0;
264          _decodeStopCorr.setDate(QDate());
265          _decodeStopCorr.setTime(QTime());
266          _decodeStartCorr = QDateTime::currentDateTime();
267        }
268      }
269      else {
270
271        // End corrupt threshold
272        // ---------------------
273        if ( _decodeStartCorr.isValid() ) {
274          _endDateTimeCorr = QDateTime::currentDateTime();
275          if ( _begDateTimeCorr.secsTo(QDateTime::currentDateTime()) > _adviseReco * 60 ) {
276            _endDateCorr = _begDateTimeCorr.toUTC().date().toString("yy-MM-dd");
277            _endTimeCorr = _begDateTimeCorr.toUTC().time().toString("hh:mm:ss");
278            emit(newMessage((_staID + ": Recovery threshold exceeded, corruption ended "
279                        + _endDateCorr + " " + _endTimeCorr + " UTC").toLatin1(), true));
280            callScript(("End_Corrupted "
281                        + _endDateCorr + " " + _endTimeCorr + " UTC Begin was "
282                        + _begDateCorr + " " + _begTimeCorr + " UTC").toLatin1());
283            _decodeStartCorr.setDate(QDate());
284            _decodeStartCorr.setTime(QTime());
285            _decodeStopCorr = QDateTime::currentDateTime();
286            _secFail = 0;
287          }
288        }
289      }
290      _checkSeg = false;
291    }
292  }
293
294  // End outage threshold
295  // --------------------
296  if (_fromReconnect) {
297    _begDateTimeOut = QDateTime::currentDateTime();
298  }
299  _fromReconnect = false;
300
301  if ( _decodeStart.isValid() ) {
302    _endDateTimeOut = QDateTime::currentDateTime();
303    if ( _begDateTimeOut.secsTo(QDateTime::currentDateTime()) >  _adviseReco * 60 ) {
304      _endDateOut = _begDateTimeOut.toUTC().date().toString("yy-MM-dd");
305      _endTimeOut = _begDateTimeOut.toUTC().time().toString("hh:mm:ss");
306      emit(newMessage((_staID + ": Recovery threshold exceeded, outage ended "
307                    + _endDateOut + " " + _endTimeOut + " UTC").toLatin1(), true));
308      callScript(("End_Outage "
309                    + _endDateOut + " " + _endTimeOut + " UTC Begin was "
310                    + _begDateOut + " " + _begTimeOut + " UTC").toLatin1());
311      _decodeStart.setDate(QDate());
312      _decodeStart.setTime(QTime());
313      _decodeStop = QDateTime::currentDateTime();
314    }
315  }
316}
317
318// Perform latency checks (observations)
319//////////////////////////////////////////////////////////////////////////////
320void latencyChecker::checkObsLatency(const QList<t_satObs>& obsList) {
321
322  if (_miscIntr > 0 ) {
323    t_latency& l = _lObs;
324    l._type =  "Observations";
325    QListIterator<t_satObs> it(obsList);
326    while (it.hasNext()) {
327      const t_satObs& obs = it.next();
328      bool wrongObservationEpoch = checkForWrongObsEpoch(obs._time);
329      if (wrongObservationEpoch) {
330        emit( newMessage(QString("%1: Wrong observation epoch from %3 ")
331            .arg(_staID.data())
332            .arg(obs._prn.toString().data())
333            .toAscii(), true) );
334      }
335      l._newSec = static_cast<int>(nint(obs._time.gpssec()*10));
336      if (l._newSec > l._oldSec && !wrongObservationEpoch) {
337        if (l._newSec % (_miscIntr * 10) < l._oldSec % (_miscIntr * 10)) {
338          if (l._numLat > 0) {
339            if (l._meanDiff > 0.0) {
340              if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
341                emit( newMessage(QString("%1 %2: Mean latency %3 sec, min %4, max %5, rms %6, %7 epochs, %8 gaps")
342                  .arg(_staID.data())
343                  .arg(l._type.data())
344                  .arg(int(l._sumLat/l._numLat*100)/100.)
345                  .arg(int(l._minLat*100)/100.)
346                  .arg(int(l._maxLat*100)/100.)
347                  .arg(int((sqrt((l._sumLatQ - l._sumLat * l._sumLat / l._numLat)/l._numLat))*100)/100.)
348                  .arg(l._numLat)
349                  .arg(l._numGaps)
350                  .toLatin1(), true) );
351              }
352            } else {
353              if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
354                emit( newMessage(QString("%1 %2: Mean latency %3 sec, min %4, max %5, rms %6, %7 epochs")
355                  .arg(_staID.data())
356                  .arg(l._type.data())
357                  .arg(int(l._sumLat/l._numLat*100)/100.)
358                  .arg(int(l._minLat*100)/100.)
359                  .arg(int(l._maxLat*100)/100.)
360                  .arg(int((sqrt((l._sumLatQ - l._sumLat * l._sumLat / l._numLat)/l._numLat))*100)/100.)
361                  .arg(l._numLat)
362                  .toLatin1(), true) );
363              }
364            }
365            l._meanDiff  = l._diffSec / l._numLat;
366            l.init();
367          }
368        }
369        if (l._followSec) {
370          l._diffSec += l._newSec - l._oldSec;
371          if (l._meanDiff > 0.0) {
372            if (l._newSec - l._oldSec > 1.5 * l._meanDiff) {
373              l._numGaps += 1;
374            }
375          }
376        }
377
378        // Compute the observations latency
379        // --------------------------------
380        int      week;
381        double   sec;
382        currentGPSWeeks(week, sec);
383        const double secPerWeek = 7.0 * 24.0 * 3600.0;
384        if (week < int(obs._time.gpsw())) {
385          week += 1;
386          sec  -= secPerWeek;
387        }
388        if (week > int(obs._time.gpsw())) {
389          week -= 1;
390          sec  += secPerWeek;
391        }
392        l._curLat   = sec - obs._time.gpssec();
393        l._sumLat  += l._curLat;
394        l._sumLatQ += l._curLat * l._curLat;
395        if (l._curLat < l._minLat) {
396          l._minLat = l._curLat;
397        }
398        if (l._curLat >= l._maxLat) {
399          l._maxLat = l._curLat;
400        }
401        l._numLat += 1;
402        l._followSec = true;
403      }
404      l._oldSec = l._newSec;
405    }
406    _lObs = l;
407    setCurrentLatency(l._curLat);
408  }
409}
410
411// Perform latency checks (corrections)
412//////////////////////////////////////////////////////////////////////////////
413void latencyChecker::checkCorrLatency(int corrGPSEpochTime, int type) {
414  if (corrGPSEpochTime < 0) {
415    return;
416  }
417
418  t_latency& l = _lOrb; // init
419  switch (type) {
420    case 1057: case 1063: case 1240: case 1246: case 1252: case 1258:
421      l = _lOrb; l._type =  "Orbit";
422      break;
423    case 1058: case 1064: case 1241: case 1247: case 1253: case 1259:
424      l = _lClk; l._type =  "Clock";
425      break;
426    case 1060: case 1066: case 1243: case 1249: case 1255: case 1261:
427      l = _lClkOrb; l._type = "Clock&Orbit";
428      break;
429    case 1059: case 1065: case 1242: case 1248: case 1254: case 1260:
430      l = _lCb; l._type = "CodeBiases";
431      break;
432    case 1265: case 1266: case 1267: case 1268: case 1269: case 1270:
433      l = _lPb; l._type = "PhaseBiases";
434      break;
435    case 1264:
436      l = _lVtec; l._type = "VTEC";
437      break;
438    case 1061: case 1067: case 1244: case 1250: case 1256: case 1262:
439      l = _lUra; l._type = "URA";
440      break;
441    case 1062: case 1068: case 1245: case 1251: case 1257: case 1263:
442      l = _lHr; l._type = "HrClock";
443      break;
444    default:
445      return;
446  }
447
448  if (_miscIntr > 0) {
449    l._newSec = corrGPSEpochTime;
450    if (l._newSec > l._oldSec) {
451      if (int(l._newSec) % _miscIntr < int(l._oldSec) % _miscIntr) {
452        if (l._numLat>0) {
453          QString late;
454          if (l._meanDiff>0.) {
455            late = QString(" %1: Mean latency %2 sec, min %3, max %4, rms %5, %6 epochs, %7 gaps")
456            .arg(l._type.data())
457            .arg(int(l._sumLat/l._numLat*100)/100.)
458            .arg(int(l._minLat*100)/100.)
459            .arg(int(l._maxLat*100)/100.)
460            .arg(int((sqrt((l._sumLatQ - l._sumLat * l._sumLat / l._numLat)/l._numLat))*100)/100.)
461            .arg(l._numLat)
462            .arg(l._numGaps);
463            if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
464              emit(newMessage(QString(_staID + late ).toLatin1(), true) );
465            }
466          }
467          else {
468            late = QString(" %1: Mean latency %2 sec, min %3, max %4, rms %5, %6 epochs")
469            .arg(l._type.data())
470            .arg(int(l._sumLat/l._numLat*100)/100.)
471            .arg(int(l._minLat*100)/100.)
472            .arg(int(l._maxLat*100)/100.)
473            .arg(int((sqrt((l._sumLatQ - l._sumLat * l._sumLat / l._numLat)/l._numLat))*100)/100.)
474            .arg(l._numLat);
475            if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
476            emit(newMessage(QString(_staID + late ).toLatin1(), true) );
477            }
478          }
479        }
480        l._meanDiff = int(l._diffSec)/l._numLat;
481        l.init();
482      }
483      if (l._followSec) {
484        l._diffSec += l._newSec - l._oldSec;
485        if (l._meanDiff>0.) {
486          if (l._newSec - l._oldSec > 1.5 * l._meanDiff) {
487            l._numGaps += 1;
488          }
489        }
490      }
491
492      // Compute the observations latency
493      // --------------------------------
494      int week;
495      double sec;
496      currentGPSWeeks(week, sec);
497      double dt = fabs(sec - l._newSec);
498      const double secPerWeek = 7.0 * 24.0 * 3600.0;
499      if (dt > 0.5 * secPerWeek) {
500        if (sec > l._newSec) {
501          sec  -= secPerWeek;
502        } else {
503          sec  += secPerWeek;
504        }
505      }
506      l._curLat   = sec - l._newSec;
507      l._sumLat  += l._curLat;
508      l._sumLatQ += l._curLat * l._curLat;
509      if (l._curLat < l._minLat) {
510        l._minLat = l._curLat;
511      }
512      if (l._curLat >= l._maxLat) {
513        l._maxLat = l._curLat;
514      }
515      l._numLat += 1;
516      l._followSec = true;
517      setCurrentLatency(l._curLat);
518    }
519    l._oldSec = l._newSec;
520  }
521
522  switch (type) {
523     case 1057: case 1063: case 1240: case 1246: case 1252: case 1258:
524       _lOrb = l;
525       break;
526     case 1058: case 1064: case 1241: case 1247: case 1253: case 1259:
527       _lClk = l;
528       break;
529     case 1060: case 1066: case 1243: case 1249: case 1255: case 1261:
530       _lClkOrb = l;
531       break;
532     case 1059: case 1065: case 1242: case 1248: case 1254: case 1260:
533       _lCb = l;
534       break;
535     case 1265: case 1266: case 1267: case 1268: case 1269: case 1270:
536       _lPb = l;
537       break;
538     case 1264:
539       _lVtec = l;
540       break;
541     case 1061: case 1067: case 1244: case 1250: case 1256: case 1262:
542       _lUra = l;
543       break;
544     case 1062: case 1068: case 1245: case 1251: case 1257: case 1263:
545       _lHr = l;
546       break;
547   }
548}
549
550// Call advisory notice script
551////////////////////////////////////////////////////////////////////////////
552void latencyChecker::callScript(const char* comment) {
553  if (!_adviseScript.isEmpty()) {
554#ifdef WIN32
555    Sleep(1);
556    QProcess::startDetached(_adviseScript, QStringList() << _staID << comment) ;
557#else
558    sleep(1);
559    QProcess::startDetached("nohup", QStringList() << _adviseScript << _staID << comment) ;
560#endif
561  }
562}
Note: See TracBrowser for help on using the repository browser.