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

Last change on this file since 8669 was 8669, checked in by stuerze, 4 months ago

check with respect to wrong observation epochs is improved

File size: 18.3 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").toAscii(), true));
181      callScript(("Begin_Outage "
182                    + _begDateOut + " " + _begTimeOut + " UTC").toAscii());
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").toAscii(), true));
260          callScript(("Begin_Corrupted "
261                    + _begDateCorr + " " + _begTimeCorr + " UTC").toAscii());
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").toAscii(), true));
280            callScript(("End_Corrupted "
281                        + _endDateCorr + " " + _endTimeCorr + " UTC Begin was "
282                        + _begDateCorr + " " + _begTimeCorr + " UTC").toAscii());
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").toAscii(), true));
308      callScript(("End_Outage "
309                    + _endDateOut + " " + _endTimeOut + " UTC Begin was "
310                    + _begDateOut + " " + _begTimeOut + " UTC").toAscii());
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                  .toAscii(), 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                  .toAscii(), 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  t_latency& l = _lOrb; // init
418  switch (type) {
419    case 1057: case 1063: case 1240: case 1246: case 1252: case 1258:
420      l = _lOrb; l._type =  "Orbit";
421      break;
422    case 1058: case 1064: case 1241: case 1247: case 1253: case 1259:
423      l = _lClk; l._type =  "Clock";
424      break;
425    case 1060: case 1066: case 1243: case 1249: case 1255: case 1261:
426      l = _lClkOrb; l._type = "Clock&Orbit";
427      break;
428    case 1059: case 1065: case 1242: case 1248: case 1254: case 1260:
429      l = _lCb; l._type = "CodeBiases";
430      break;
431    case 1265: case 1266: case 1267: case 1268: case 1269: case 1270:
432      l = _lPb; l._type = "PhaseBiases";
433      break;
434    case 1264:
435      l = _lVtec; l._type = "VTEC";
436      break;
437    case 1061: case 1067: case 1244: case 1250: case 1256: case 1262:
438      l = _lUra; l._type = "URA";
439      break;
440    case 1062: case 1068: case 1245: case 1251: case 1257: case 1263:
441      l = _lHr; l._type = "HrClock";
442      break;
443    default:
444      return;
445  }
446
447  if (_miscIntr > 0) {
448    l._newSec = corrGPSEpochTime;
449    if (l._newSec > l._oldSec) {
450      if (int(l._newSec) % _miscIntr < int(l._oldSec) % _miscIntr) {
451        if (l._numLat>0) {
452          QString late;
453          if (l._meanDiff>0.) {
454            late = QString(" %1: Mean latency %2 sec, min %3, max %4, rms %5, %6 epochs, %7 gaps")
455            .arg(l._type.data())
456            .arg(int(l._sumLat/l._numLat*100)/100.)
457            .arg(int(l._minLat*100)/100.)
458            .arg(int(l._maxLat*100)/100.)
459            .arg(int((sqrt((l._sumLatQ - l._sumLat * l._sumLat / l._numLat)/l._numLat))*100)/100.)
460            .arg(l._numLat)
461            .arg(l._numGaps);
462            if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
463              emit(newMessage(QString(_staID + late ).toAscii(), true) );
464            }
465          }
466          else {
467            late = QString(" %1: Mean latency %2 sec, min %3, max %4, rms %5, %6 epochs")
468            .arg(l._type.data())
469            .arg(int(l._sumLat/l._numLat*100)/100.)
470            .arg(int(l._minLat*100)/100.)
471            .arg(int(l._maxLat*100)/100.)
472            .arg(int((sqrt((l._sumLatQ - l._sumLat * l._sumLat / l._numLat)/l._numLat))*100)/100.)
473            .arg(l._numLat);
474            if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
475            emit(newMessage(QString(_staID + late ).toAscii(), true) );
476            }
477          }
478        }
479        l._meanDiff = int(l._diffSec)/l._numLat;
480        l.init();
481      }
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    switch (type) {
522       case 1057: case 1063: case 1240: case 1246: case 1252: case 1258:
523         _lOrb = l;
524         break;
525       case 1058: case 1064: case 1241: case 1247: case 1253: case 1259:
526         _lClk = l;
527         break;
528       case 1060: case 1066: case 1243: case 1249: case 1255: case 1261:
529         _lClkOrb = l;
530         break;
531       case 1059: case 1065: case 1242: case 1248: case 1254: case 1260:
532         _lCb = l;
533         break;
534       case 1265: case 1266: case 1267: case 1268: case 1269: case 1270:
535         _lPb = l;
536         break;
537       case 1264:
538         _lVtec = l;
539         break;
540       case 1061: case 1067: case 1244: case 1250: case 1256: case 1262:
541         _lUra = l;
542         break;
543       case 1062: case 1068: case 1245: case 1251: case 1257: case 1263:
544         _lHr = l;
545         break;
546     }
547}
548
549// Call advisory notice script
550////////////////////////////////////////////////////////////////////////////
551void latencyChecker::callScript(const char* comment) {
552  if (!_adviseScript.isEmpty()) {
553#ifdef WIN32
554    Sleep(1);
555    QProcess::startDetached(_adviseScript, QStringList() << _staID << comment) ;
556#else
557    sleep(1);
558    QProcess::startDetached("nohup", QStringList() << _adviseScript << _staID << comment) ;
559#endif
560  }
561}
Note: See TracBrowser for help on using the repository browser.