source: ntrip/trunk/BNC/latencychecker.cpp@ 1566

Last change on this file since 1566 was 1566, checked in by mervart, 15 years ago

* empty log message *

File size: 13.6 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#include "latencychecker.h"
44#include "bncapp.h"
45#include "bncutils.h"
46#include "bncsettings.h"
47
48using namespace std;
49
50// Constructor
51//////////////////////////////////////////////////////////////////////////////
52latencyChecker::latencyChecker(QByteArray staID) {
53
54 _staID = staID;
55
56 bncApp* app = (bncApp*) qApp;
57 connect(this, SIGNAL(newMessage(QByteArray,bool)),
58 app, SLOT(slotMessage(const QByteArray,bool)));
59
60 bncSettings settings;
61
62 // Notice threshold
63 // ----------------
64 QString obsRate = settings.value("obsRate").toString();
65 _inspSegm = 0;
66 if ( obsRate.isEmpty() ) {
67 _inspSegm = 0;
68 }
69 else if ( obsRate.indexOf("5 Hz") != -1 ) {
70 _inspSegm = 20;
71 }
72 else if ( obsRate.indexOf("1 Hz") != -1 ) {
73 _inspSegm = 10;
74 }
75 else if ( obsRate.indexOf("0.5 Hz") != -1 ) {
76 _inspSegm = 20;
77 }
78 else if ( obsRate.indexOf("0.2 Hz") != -1 ) {
79 _inspSegm = 40;
80 }
81 else if ( obsRate.indexOf("0.1 Hz") != -1 ) {
82 _inspSegm = 50;
83 }
84 _adviseFail = settings.value("adviseFail").toInt();
85 _adviseReco = settings.value("adviseReco").toInt();
86 if ( Qt::CheckState(settings.value("makePause").toInt()) == Qt::Checked) {
87 _makePause = true;
88 }
89 else {
90 _makePause = false;
91 }
92 _adviseScript = settings.value("adviseScript").toString();
93 expandEnvVar(_adviseScript);
94
95 // Latency interval/average
96 // ------------------------
97 _perfIntr = 0;
98 QString perfIntr = settings.value("perfIntr").toString();
99 if ( perfIntr.isEmpty() ) {
100 _perfIntr = 0;
101 }
102 else if ( perfIntr.indexOf("2 sec") != -1 ) {
103 _perfIntr = 2;
104 }
105 else if ( perfIntr.indexOf("10 sec") != -1 ) {
106 _perfIntr = 10;
107 }
108 else if ( perfIntr.indexOf("1 min") != -1 ) {
109 _perfIntr = 60;
110 }
111 else if ( perfIntr.indexOf("5 min") != -1 ) {
112 _perfIntr = 300;
113 }
114 else if ( perfIntr.indexOf("15 min") != -1 ) {
115 _perfIntr = 900;
116 }
117 else if ( perfIntr.indexOf("1 hour") != -1 ) {
118 _perfIntr = 3600;
119 }
120 else if ( perfIntr.indexOf("6 hours") != -1 ) {
121 _perfIntr = 21600;
122 }
123 else if ( perfIntr.indexOf("1 day") != -1 ) {
124 _perfIntr = 86400;
125 }
126
127 // RTCM message types
128 // ------------------
129 _checkMountPoint = settings.value("miscMount").toString();
130
131 // Initialize private members
132 // --------------------------
133 _maxDt = 600.0; // Check observation epoch
134 _wrongEpoch = false;
135 _decode = true;
136 _numSucc = 0;
137 _secSucc = 0;
138 _secFail = 0;
139 _initPause = 30; // Initial pause for corrupted streams
140 _currPause = 0;
141 _begCorrupt = false;
142 _endCorrupt = false;
143 _followSec = false;
144 _oldSecGPS = 0;
145 _newSecGPS = 0;
146 _numGaps = 0;
147 _diffSecGPS = 0;
148 _numLat = 0;
149 _sumLat = 0.0;
150 _sumLatQ = 0.0;
151 _meanDiff = 0.0;
152 _minLat = _maxDt;
153 _maxLat = -_maxDt;
154 _curLat = 0.0;
155
156 _decodeTime = QDateTime::currentDateTime();
157 _decodeSucc = QDateTime::currentDateTime();
158}
159
160// Destructor
161//////////////////////////////////////////////////////////////////////////////
162latencyChecker::~latencyChecker() {
163}
164
165// Perform check for outages
166//////////////////////////////////////////////////////////////////////////////
167void latencyChecker::checkOutage(bool decoded) {
168
169 // Check - once per inspect segment
170 // --------------------------------
171 if (decoded) {
172
173 _decodeTime = QDateTime::currentDateTime();
174
175 if (_numSucc > 0) {
176 _secSucc += _inspSegm;
177 _decodeSucc = QDateTime::currentDateTime();
178 if (_secSucc > _adviseReco * 60) {
179 _secSucc = _adviseReco * 60 + 1;
180 }
181 _numSucc = 0;
182 _currPause = _initPause;
183 _decodePause.setDate(QDate());
184 _decodePause.setTime(QTime());
185 }
186 else {
187 _secFail += _inspSegm;
188 _secSucc = 0;
189 if (_secFail > _adviseFail * 60) {
190 _secFail = _adviseFail * 60 + 1;
191 }
192 if (!_decodePause.isValid() || !_makePause) {
193 _decodePause = QDateTime::currentDateTime();
194 }
195 else {
196 _decodePause.setDate(QDate());
197 _decodePause.setTime(QTime());
198 _secFail = _secFail + _currPause - _inspSegm;
199 _currPause = _currPause * 2;
200 if (_currPause > 960) {
201 _currPause = 960;
202 }
203 }
204 }
205
206 // End corrupt threshold
207 // ---------------------
208 if ( _begCorrupt && !_endCorrupt && _secSucc > _adviseReco * 60 ) {
209 _endDateCor = QDateTime::currentDateTime().addSecs(- _adviseReco * 60).toUTC().date().toString("yy-MM-dd");
210 _endTimeCor = QDateTime::currentDateTime().addSecs(- _adviseReco * 60).toUTC().time().toString("hh:mm:ss");
211 emit(newMessage((_staID + ": Recovery threshold exceeded, corruption ended "
212 + _endDateCor + " " + _endTimeCor).toAscii(), true));
213 callScript(("End_Corrupted " + _endDateCor + " " + _endTimeCor + " Begin was " + _begDateCor + " " + _begTimeCor).toAscii());
214 _endCorrupt = true;
215 _begCorrupt = false;
216 _secFail = 0;
217 }
218 else {
219
220 // Begin corrupt threshold
221 // -----------------------
222 if ( !_begCorrupt && _secFail > _adviseFail * 60 ) {
223 _begDateCor = _decodeSucc.toUTC().date().toString("yy-MM-dd");
224 _begTimeCor = _decodeSucc.toUTC().time().toString("hh:mm:ss");
225 emit(newMessage((_staID + ": Failure threshold exceeded, corrupted since "
226 + _begDateCor + " " + _begTimeCor).toAscii(), true));
227 callScript(("Begin_Corrupted " + _begDateCor + " " + _begTimeCor).toAscii());
228 _begCorrupt = true;
229 _endCorrupt = false;
230 _secSucc = 0;
231 _numSucc = 0;
232 }
233 }
234 }
235
236 // End outage threshold
237 // --------------------
238 if ( _decodeStart.isValid() && _decodeStart.secsTo(QDateTime::currentDateTime()) > _adviseReco * 60 ) {
239 _decodeStart.setDate(QDate());
240 _decodeStart.setTime(QTime());
241 if (_inspSegm > 0) {
242 _endDateOut = QDateTime::currentDateTime().addSecs(- _adviseReco * 60).toUTC().date().toString("yy-MM-dd");
243 _endTimeOut = QDateTime::currentDateTime().addSecs(- _adviseReco * 60).toUTC().time().toString("hh:mm:ss");
244 emit(newMessage((_staID + ": Recovery threshold exceeded, outage ended "
245 + _endDateOut + " " + _endTimeOut).toAscii(), true));
246 callScript(("End_Outage " + _endDateOut + " " + _endTimeOut + " Begin was " + _begDateOut + " " + _begTimeOut).toAscii());
247 }
248 }
249}
250
251// Perform latency checks (observations)
252//////////////////////////////////////////////////////////////////////////////
253void latencyChecker::checkObsLatency(const QList<p_obs>& obsList) {
254
255 if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
256 if (_perfIntr > 0 ) {
257
258 QListIterator<p_obs> it(obsList);
259 while (it.hasNext()) {
260 p_obs obs = it.next();
261
262 _newSecGPS = static_cast<int>(obs->_o.GPSWeeks);
263 if (_newSecGPS != _oldSecGPS) {
264 if (_newSecGPS % _perfIntr < _oldSecGPS % _perfIntr) {
265 if (_numLat > 0) {
266 if (_meanDiff > 0.0) {
267 emit( newMessage(QString("%1: Mean latency %2 sec, min %3, max %4, rms %5, %6 epochs, %7 gaps")
268 .arg(_staID.data())
269 .arg(int(_sumLat/_numLat*100)/100.)
270 .arg(int(_minLat*100)/100.)
271 .arg(int(_maxLat*100)/100.)
272 .arg(int((sqrt((_sumLatQ - _sumLat * _sumLat / _numLat)/_numLat))*100)/100.)
273 .arg(_numLat)
274 .arg(_numGaps)
275 .toAscii(), true) );
276 } else {
277 emit( newMessage(QString("%1: Mean latency %2 sec, min %3, max %4, rms %5, %6 epochs")
278 .arg(_staID.data())
279 .arg(int(_sumLat/_numLat*100)/100.)
280 .arg(int(_minLat*100)/100.)
281 .arg(int(_maxLat*100)/100.)
282 .arg(int((sqrt((_sumLatQ - _sumLat * _sumLat / _numLat)/_numLat))*100)/100.)
283 .arg(_numLat)
284 .toAscii(), true) );
285 }
286 }
287 _meanDiff = _diffSecGPS / _numLat;
288 _diffSecGPS = 0;
289 _numGaps = 0;
290 _sumLat = 0.0;
291 _sumLatQ = 0.0;
292 _numLat = 0;
293 _minLat = _maxDt;
294 _maxLat = -_maxDt;
295 }
296 if (_followSec) {
297 _diffSecGPS += _newSecGPS - _oldSecGPS;
298 if (_meanDiff>0.) {
299 if (_newSecGPS - _oldSecGPS > 1.5 * _meanDiff) {
300 _numGaps += 1;
301 }
302 }
303 }
304
305 // Compute the observations latency
306 // --------------------------------
307 int week;
308 double sec;
309 currentGPSWeeks(week, sec);
310 const double secPerWeek = 7.0 * 24.0 * 3600.0;
311 if (week < obs->_o.GPSWeek) {
312 week += 1;
313 sec -= secPerWeek;
314 }
315 if (week > obs->_o.GPSWeek) {
316 week -= 1;
317 sec += secPerWeek;
318 }
319
320 _curLat = sec - obs->_o.GPSWeeks;
321 _sumLat += _curLat;
322 _sumLatQ += _curLat * _curLat;
323 if (_curLat < _minLat) {
324 _minLat = _curLat;
325 }
326 if (_curLat >= _maxLat) {
327 _maxLat = _curLat;
328 }
329 _numLat += 1;
330 _oldSecGPS = _newSecGPS;
331 _followSec = true;
332 }
333 }
334 }
335 }
336}
337
338// Perform latency checks (corrections)
339//////////////////////////////////////////////////////////////////////////////
340void latencyChecker::checkCorrLatency(int corrGPSEpochTime) {
341
342 if (corrGPSEpochTime < 0) {
343 return;
344 }
345
346 if ( _checkMountPoint == _staID || _checkMountPoint == "ALL" ) {
347 if (_perfIntr > 0) {
348
349 _newSecGPS = corrGPSEpochTime;
350
351 int week;
352 double sec;
353 currentGPSWeeks(week, sec);
354 double dt = fabs(sec - _newSecGPS);
355 const double secPerWeek = 7.0 * 24.0 * 3600.0;
356 if (dt > 0.5 * secPerWeek) {
357 if (sec > _newSecGPS) {
358 sec -= secPerWeek;
359 } else {
360 sec += secPerWeek;
361 }
362 }
363 if (_newSecGPS != _oldSecGPS) {
364 if (int(_newSecGPS) % _perfIntr < int(_oldSecGPS) % _perfIntr) {
365 if (_numLat>0) {
366 QString late;
367 if (_meanDiff>0.) {
368 late = QString(": Mean latency %1 sec, min %2, max %3, rms %4, %5 epochs, %6 gaps")
369 .arg(int(_sumLat/_numLat*100)/100.)
370 .arg(int(_minLat*100)/100.)
371 .arg(int(_maxLat*100)/100.)
372 .arg(int((sqrt((_sumLatQ - _sumLat * _sumLat / _numLat)/_numLat))*100)/100.)
373 .arg(_numLat)
374 .arg(_numGaps);
375 emit(newMessage(QString(_staID + late ).toAscii(), true) );
376 }
377 else {
378 late = QString(": Mean latency %1 sec, min %2, max %3, rms %4, %5 epochs")
379 .arg(int(_sumLat/_numLat*100)/100.)
380 .arg(int(_minLat*100)/100.)
381 .arg(int(_maxLat*100)/100.)
382 .arg(int((sqrt((_sumLatQ - _sumLat * _sumLat / _numLat)/_numLat))*100)/100.)
383 .arg(_numLat);
384 emit(newMessage(QString(_staID + late ).toAscii(), true) );
385 }
386 }
387 _meanDiff = int(_diffSecGPS)/_numLat;
388 _diffSecGPS = 0;
389 _numGaps = 0;
390 _sumLat = 0.0;
391 _sumLatQ = 0.0;
392 _numLat = 0;
393 _minLat = 1000.;
394 _maxLat = -1000.;
395 }
396 if (_followSec) {
397 _diffSecGPS += _newSecGPS - _oldSecGPS;
398 if (_meanDiff>0.) {
399 if (_newSecGPS - _oldSecGPS > 1.5 * _meanDiff) {
400 _numGaps += 1;
401 }
402 }
403 }
404 _curLat = sec - _newSecGPS;
405 _sumLat += _curLat;
406 _sumLatQ += _curLat * _curLat;
407 if (_curLat < _minLat) {
408 _minLat = _curLat;
409 }
410 if (_curLat >= _maxLat) {
411 _maxLat = _curLat;
412 }
413 _numLat += 1;
414 _oldSecGPS = _newSecGPS;
415 _followSec = true;
416 }
417 }
418 }
419}
420
421// Call advisory notice script
422////////////////////////////////////////////////////////////////////////////
423void latencyChecker::callScript(const char* comment) {
424 if (!_adviseScript.isEmpty()) {
425 sleep(1);
426#ifdef WIN32
427 QProcess::startDetached(_adviseScript, QStringList() << _staID << comment) ;
428#else
429 QProcess::startDetached("nohup", QStringList() << _adviseScript << _staID << comment) ;
430#endif
431 }
432}
Note: See TracBrowser for help on using the repository browser.