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

Last change on this file since 8082 was 8082, checked in by stuerze, 2 years ago

the latency checker is now able to check ssr data streams with messages containing different update rates

File size: 17.9 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; l._type =  "Observations";
324    QListIterator<t_satObs> it(obsList);
325    while (it.hasNext()) {
326      const t_satObs& obs = it.next();
327      bool wrongObservationEpoch = checkForWrongObsEpoch(obs._time);
328      l._newSec = static_cast<int>(obs._time.gpssec());
329      if (l._newSec > l._oldSec && !wrongObservationEpoch) {
330        if (l._newSec % _miscIntr < l._oldSec % _miscIntr) {
331          if (l._numLat > 0) {
332            if (l._meanDiff > 0.0) {
333              if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
334                emit( newMessage(QString("%1 %2: Mean latency %3 sec, min %4, max %5, rms %6, %7 epochs, %8 gaps")
335                  .arg(_staID.data())
336                  .arg(l._type.data())
337                  .arg(int(l._sumLat/l._numLat*100)/100.)
338                  .arg(int(l._minLat*100)/100.)
339                  .arg(int(l._maxLat*100)/100.)
340                  .arg(int((sqrt((l._sumLatQ - l._sumLat * l._sumLat / l._numLat)/l._numLat))*100)/100.)
341                  .arg(l._numLat)
342                  .arg(l._numGaps)
343                  .toAscii(), true) );
344              }
345            } else {
346              if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
347                emit( newMessage(QString("%1 %2: Mean latency %3 sec, min %4, max %5, rms %6, %7 epochs")
348                  .arg(_staID.data())
349                  .arg(l._type.data())
350                  .arg(int(l._sumLat/l._numLat*100)/100.)
351                  .arg(int(l._minLat*100)/100.)
352                  .arg(int(l._maxLat*100)/100.)
353                  .arg(int((sqrt((l._sumLatQ - l._sumLat * l._sumLat / l._numLat)/l._numLat))*100)/100.)
354                  .arg(l._numLat)
355                  .toAscii(), true) );
356              }
357            }
358          }
359          l._meanDiff  = l._diffSec / l._numLat;
360          l.init();
361        }
362        if (l._followSec) {
363          l._diffSec += l._newSec - l._oldSec;
364          if (l._meanDiff>0.) {
365            if (l._newSec - l._oldSec > 1.5 * l._meanDiff) {
366              l._numGaps += 1;
367            }
368          }
369        }
370
371        // Compute the observations latency
372        // --------------------------------
373        int      week;
374        double   sec;
375        currentGPSWeeks(week, sec);
376        const double secPerWeek = 7.0 * 24.0 * 3600.0;
377        if (week < int(obs._time.gpsw())) {
378          week += 1;
379          sec  -= secPerWeek;
380        }
381        if (week > int(obs._time.gpsw())) {
382          week -= 1;
383          sec  += secPerWeek;
384        }
385        l._curLat   = sec - obs._time.gpssec();
386        l._sumLat  += l._curLat;
387        l._sumLatQ += l._curLat * l._curLat;
388        if (l._curLat < l._minLat) {
389          l._minLat = l._curLat;
390        }
391        if (l._curLat >= l._maxLat) {
392          l._maxLat = l._curLat;
393        }
394        l._numLat += 1;
395        l._oldSec    = l._newSec;
396        l._followSec = true;
397      }
398    }
399    _lObs = l;
400    setCurrentLatency(l._curLat);
401  }
402}
403
404// Perform latency checks (corrections)
405//////////////////////////////////////////////////////////////////////////////
406void latencyChecker::checkCorrLatency(int corrGPSEpochTime, int type) {
407  if (corrGPSEpochTime < 0) {
408    return ;
409  }
410  t_latency& l = _lOrb; // init
411  switch (type) {
412    case 1057: case 1063: case 1240: case 1246: case 1252: case 1258:
413      l = _lOrb; l._type =  "Orbit";
414      break;
415    case 1058: case 1064: case 1241: case 1247: case 1253: case 1259:
416      l = _lClk; l._type =  "Clock";
417      break;
418    case 1060: case 1066: case 1243: case 1249: case 1255: case 1261:
419      l = _lClkOrb; l._type = "Clock&Orbit";
420      break;
421    case 1059: case 1065: case 1242: case 1248: case 1254: case 1260:
422      l = _lCb; l._type = "Code Biases";
423      break;
424    case 1265: case 1266: case 1267: case 1268: case 1269: case 1270:
425      l = _lPb; l._type = "Phase Biases";
426      break;
427    case 1264:
428      l = _lVtec; l._type = "VTEC";
429      break;
430    case 1061: case 1067: case 1244: case 1250: case 1256: case 1262:
431      l = _lUra; l._type = "URA";
432      break;
433    case 1062: case 1068: case 1245: case 1251: case 1257: case 1263:
434      l = _lHr; l._type = "Hr Clock";
435      break;
436    default:
437      return;
438  }
439
440  if (_miscIntr > 0) {
441    l._newSec = corrGPSEpochTime;
442    int week;
443    double sec;
444    currentGPSWeeks(week, sec);
445    double dt = fabs(sec - l._newSec);
446    const double secPerWeek = 7.0 * 24.0 * 3600.0;
447    if (dt > 0.5 * secPerWeek) {
448      if (sec > l._newSec) {
449        sec  -= secPerWeek;
450      } else {
451        sec  += secPerWeek;
452      }
453    }
454    if (l._newSec > l._oldSec) {
455      if (int(l._newSec) % _miscIntr < int(l._oldSec) % _miscIntr) {
456        if (l._numLat>0) {
457          QString late;
458          if (l._meanDiff>0.) {
459            late = QString(" %1: Mean latency %2 sec, min %3, max %4, rms %5, %6 epochs, %7 gaps")
460            .arg(l._type.data())
461            .arg(int(l._sumLat/l._numLat*100)/100.)
462            .arg(int(l._minLat*100)/100.)
463            .arg(int(l._maxLat*100)/100.)
464            .arg(int((sqrt((l._sumLatQ - l._sumLat * l._sumLat / l._numLat)/l._numLat))*100)/100.)
465            .arg(l._numLat)
466            .arg(l._numGaps);
467            if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
468              emit(newMessage(QString(_staID + late ).toAscii(), true) );
469            }
470          }
471          else {
472            late = QString(" %1: Mean latency %2 sec, min %3, max %4, rms %5, %6 epochs")
473            .arg(l._type.data())
474            .arg(int(l._sumLat/l._numLat*100)/100.)
475            .arg(int(l._minLat*100)/100.)
476            .arg(int(l._maxLat*100)/100.)
477            .arg(int((sqrt((l._sumLatQ - l._sumLat * l._sumLat / l._numLat)/l._numLat))*100)/100.)
478            .arg(l._numLat);
479            if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
480            emit(newMessage(QString(_staID + late ).toAscii(), true) );
481            }
482          }
483        }
484        l._meanDiff = int(l._diffSec)/l._numLat;
485        l.init();
486      }
487
488      if (l._followSec) {
489        l._diffSec += l._newSec - l._oldSec;
490        if (l._meanDiff>0.) {
491          if (l._newSec - l._oldSec > 1.5 * l._meanDiff) {
492            l._numGaps += 1;
493          }
494        }
495      }
496      l._curLat   = sec - l._newSec;
497      l._sumLat  += l._curLat;
498      l._sumLatQ += l._curLat * l._curLat;
499      if (l._curLat < l._minLat) {
500        l._minLat = l._curLat;
501      }
502      if (l._curLat >= l._maxLat) {
503        l._maxLat = l._curLat;
504      }
505      l._numLat += 1;
506      l._oldSec = l._newSec;
507      l._followSec = true;
508      setCurrentLatency(l._curLat);
509    }
510    switch (type) {
511       case 1057: case 1063: case 1240: case 1246: case 1252: case 1258:
512         _lOrb = l;
513         break;
514       case 1058: case 1064: case 1241: case 1247: case 1253: case 1259:
515         _lClk = l;
516         break;
517       case 1060: case 1066: case 1243: case 1249: case 1255: case 1261:
518         _lClkOrb = l;
519         break;
520       case 1059: case 1065: case 1242: case 1248: case 1254: case 1260:
521         _lCb = l;
522         break;
523       case 1265: case 1266: case 1267: case 1268: case 1269: case 1270:
524         _lPb = l;
525         break;
526       case 1264:
527         _lVtec = l;
528         break;
529       case 1061: case 1067: case 1244: case 1250: case 1256: case 1262:
530         _lUra = l;
531         break;
532       case 1062: case 1068: case 1245: case 1251: case 1257: case 1263:
533         _lHr = l;
534         break;
535     }
536  }
537}
538
539// Call advisory notice script
540////////////////////////////////////////////////////////////////////////////
541void latencyChecker::callScript(const char* comment) {
542  if (!_adviseScript.isEmpty()) {
543#ifdef WIN32
544    Sleep(1);
545    QProcess::startDetached(_adviseScript, QStringList() << _staID << comment) ;
546#else
547    sleep(1);
548    QProcess::startDetached("nohup", QStringList() << _adviseScript << _staID << comment) ;
549#endif
550  }
551}
Note: See TracBrowser for help on using the repository browser.