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

Last change on this file since 8372 was 8372, checked in by stuerze, 14 months ago

minor changes to allow 10 Hz observation data processing (latency checker) and resampling (RINEX files, feed engine)

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