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

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

* empty log message *

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