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

Last change on this file since 8983 was 8678, checked in by stuerze, 5 years ago

minor changes

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 .toLatin1(), 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.