source: ntrip/trunk/BNC/src/bncwindow.cpp@ 5965

Last change on this file since 5965 was 5965, checked in by mervart, 10 years ago
File size: 120.3 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: bncWindow
30 *
31 * Purpose: This class implements the main application window.
32 *
33 * Author: L. Mervart
34 *
35 * Created: 24-Dec-2005
36 *
37 * Changes:
38 *
39 * -----------------------------------------------------------------------*/
40
41#include <iostream>
42
43#include <unistd.h>
44#include "bncwindow.h"
45#include "bnccore.h"
46#include "bncgetthread.h"
47#include "bnctabledlg.h"
48#include "bncipport.h"
49#include "bncudpport.h"
50#include "bncserialport.h"
51#include "bnchlpdlg.h"
52#include "bnchtml.h"
53#include "bnctableitem.h"
54#include "bncsettings.h"
55#include "bncfigure.h"
56#include "bncfigurelate.h"
57#include "bncfigureppp.h"
58#include "bncversion.h"
59#include "bncbytescounter.h"
60#include "bncsslconfig.h"
61#include "upload/bnccustomtrafo.h"
62#include "upload/bncephuploadcaster.h"
63#include "qtfilechooser.h"
64#include "reqcdlg.h"
65#include "bncmap.h"
66#include "rinex/reqcedit.h"
67#include "rinex/reqcanalyze.h"
68#ifdef QT_WEBKIT
69# include "map/bncmapwin.h"
70#endif
71
72using namespace std;
73
74#ifdef GNSSCENTER_PLUGIN
75Q_EXPORT_PLUGIN2(gnsscenter_bnc, t_bncFactory)
76#endif
77
78// Constructor
79////////////////////////////////////////////////////////////////////////////
80bncWindow::bncWindow() {
81
82#ifdef GNSSCENTER_PLUGIN
83 BNC_CORE->setConfFileName("");
84#endif
85
86 _caster = 0;
87 _casterEph = 0;
88
89 _bncFigure = new bncFigure(this);
90 _bncFigureLate = new bncFigureLate(this);
91 _bncFigurePPP = new bncFigurePPP(this);
92
93 connect(BNC_CORE, SIGNAL(newPosition(QByteArray, bncTime, QVector<double>)),
94 _bncFigurePPP, SLOT(slotNewPosition(QByteArray, bncTime, QVector<double>)));
95
96 connect(BNC_CORE, SIGNAL(progressRnxPPP(int)), this, SLOT(slotPostProgress(int)));
97 connect(BNC_CORE, SIGNAL(finishedRnxPPP()), this, SLOT(slotFinishedRnxPPP()));
98
99 _runningRealTime = false;
100 _runningPostProcessingReqc = false;
101 _reqcActionComboBox = 0; // necessary for enableStartStop()
102
103 _mapWin = 0;
104
105 int ww = QFontMetrics(this->font()).width('w');
106
107 static const QStringList labels = QString("account, Streams: resource loader / mountpoint, decoder, lat, long, nmea, ntrip, bytes").split(",");
108
109 setMinimumSize(85*ww, 65*ww);
110
111 setWindowTitle(tr("BKG Ntrip Client (BNC) Version " BNCVERSION));
112
113 connect(BNC_CORE, SIGNAL(newMessage(QByteArray,bool)),
114 this, SLOT(slotWindowMessage(QByteArray,bool)));
115
116 // Create Actions
117 // --------------
118 _actHelp = new QAction(tr("&Help Contents"),this);
119 connect(_actHelp, SIGNAL(triggered()), SLOT(slotHelp()));
120
121 _actAbout = new QAction(tr("&About BNC"),this);
122 connect(_actAbout, SIGNAL(triggered()), SLOT(slotAbout()));
123
124 _actFlowchart = new QAction(tr("&Flow Chart"),this);
125 connect(_actFlowchart, SIGNAL(triggered()), SLOT(slotFlowchart()));
126
127 _actFontSel = new QAction(tr("Select &Font"),this);
128 connect(_actFontSel, SIGNAL(triggered()), SLOT(slotFontSel()));
129
130 _actSaveOpt = new QAction(tr("&Reread && Save Configuration"),this);
131 connect(_actSaveOpt, SIGNAL(triggered()), SLOT(slotSaveOptions()));
132
133 _actQuit = new QAction(tr("&Quit"),this);
134 connect(_actQuit, SIGNAL(triggered()), SLOT(close()));
135
136 _actAddMountPoints = new QAction(tr("Add &Stream"),this);
137 connect(_actAddMountPoints, SIGNAL(triggered()), SLOT(slotAddMountPoints()));
138
139 _actDeleteMountPoints = new QAction(tr("&Delete Stream"),this);
140 connect(_actDeleteMountPoints, SIGNAL(triggered()), SLOT(slotDeleteMountPoints()));
141 _actDeleteMountPoints->setEnabled(false);
142
143 _actMapMountPoints = new QAction(tr("&Map"),this);
144 connect(_actMapMountPoints, SIGNAL(triggered()), SLOT(slotMapMountPoints()));
145
146 _actStart = new QAction(tr("Sta&rt"),this);
147 connect(_actStart, SIGNAL(triggered()), SLOT(slotStart()));
148
149 _actStop = new QAction(tr("Sto&p"),this);
150 connect(_actStop, SIGNAL(triggered()), SLOT(slotStop()));
151
152 _actwhatsthis= new QAction(tr("Help ?=Shift+F1"),this);
153 connect(_actwhatsthis, SIGNAL(triggered()), SLOT(slotWhatsThis()));
154
155 CreateMenu();
156 AddToolbar();
157
158 bncSettings settings;
159
160 // Network Options
161 // ---------------
162 _proxyHostLineEdit = new QLineEdit(settings.value("proxyHost").toString());
163 _proxyPortLineEdit = new QLineEdit(settings.value("proxyPort").toString());
164
165 connect(_proxyHostLineEdit, SIGNAL(textChanged(const QString &)),
166 this, SLOT(slotBncTextChanged()));
167
168 _sslCaCertPathLineEdit = new QLineEdit(settings.value("sslCaCertPath").toString());
169 _ignoreSslErrorsCheckBox = new QCheckBox();
170 _ignoreSslErrorsCheckBox->setCheckState(Qt::CheckState(
171 settings.value("ignoreSslErrors").toInt()));
172
173 // General Options
174 // ---------------
175 _logFileLineEdit = new QLineEdit(settings.value("logFile").toString());
176 _rawOutFileLineEdit = new QLineEdit(settings.value("rawOutFile").toString());
177 _rnxAppendCheckBox = new QCheckBox();
178 _rnxAppendCheckBox->setCheckState(Qt::CheckState(
179 settings.value("rnxAppend").toInt()));
180 _onTheFlyComboBox = new QComboBox();
181 _onTheFlyComboBox->setEditable(false);
182 _onTheFlyComboBox->addItems(QString("1 day,1 hour,5 min,1 min").split(","));
183 int ii = _onTheFlyComboBox->findText(settings.value("onTheFlyInterval").toString());
184 if (ii != -1) {
185 _onTheFlyComboBox->setCurrentIndex(ii);
186 }
187 _autoStartCheckBox = new QCheckBox();
188 _autoStartCheckBox->setCheckState(Qt::CheckState(
189 settings.value("autoStart").toInt()));
190
191 // RINEX Observations Options
192 // --------------------------
193 _rnxPathLineEdit = new QLineEdit(settings.value("rnxPath").toString());
194 _rnxIntrComboBox = new QComboBox();
195 _rnxIntrComboBox->setEditable(false);
196 _rnxIntrComboBox->addItems(QString("1 min,2 min,5 min,10 min,15 min,30 min,1 hour,1 day").split(","));
197 ii = _rnxIntrComboBox->findText(settings.value("rnxIntr").toString());
198 if (ii != -1) {
199 _rnxIntrComboBox->setCurrentIndex(ii);
200 }
201 _rnxSamplSpinBox = new QSpinBox();
202 _rnxSamplSpinBox->setMinimum(0);
203 _rnxSamplSpinBox->setMaximum(60);
204 _rnxSamplSpinBox->setSingleStep(5);
205 _rnxSamplSpinBox->setValue(settings.value("rnxSampl").toInt());
206 _rnxSamplSpinBox->setSuffix(" sec");
207 _rnxSkelLineEdit = new QLineEdit(settings.value("rnxSkel").toString());
208 _rnxSkelLineEdit->setMaximumWidth(5*ww);
209 _rnxScrpLineEdit = new QLineEdit(settings.value("rnxScript").toString());
210 _rnxV3CheckBox = new QCheckBox();
211 _rnxV3CheckBox->setCheckState(Qt::CheckState(settings.value("rnxV3").toInt()));
212
213 connect(_rnxPathLineEdit, SIGNAL(textChanged(const QString &)),
214 this, SLOT(slotBncTextChanged()));
215
216 // RINEX Ephemeris Options
217 // -----------------------
218 _ephPathLineEdit = new QLineEdit(settings.value("ephPath").toString());
219 _ephIntrComboBox = new QComboBox();
220 _ephIntrComboBox->setEditable(false);
221 _ephIntrComboBox->addItems(QString("1 min,2 min,5 min,10 min,15 min,30 min,1 hour,1 day").split(","));
222 int jj = _ephIntrComboBox->findText(settings.value("ephIntr").toString());
223 if (jj != -1) {
224 _ephIntrComboBox->setCurrentIndex(jj);
225 }
226 _outEphPortLineEdit = new QLineEdit(settings.value("outEphPort").toString());
227 _ephV3CheckBox = new QCheckBox();
228 _ephV3CheckBox->setCheckState(Qt::CheckState(settings.value("ephV3").toInt()));
229
230 connect(_outEphPortLineEdit, SIGNAL(textChanged(const QString &)),
231 this, SLOT(slotBncTextChanged()));
232
233 connect(_ephPathLineEdit, SIGNAL(textChanged(const QString &)),
234 this, SLOT(slotBncTextChanged()));
235
236 // Broadcast Corrections Options
237 // -----------------------------
238 _corrPathLineEdit = new QLineEdit(settings.value("corrPath").toString());
239 _corrIntrComboBox = new QComboBox();
240 _corrIntrComboBox->setEditable(false);
241 _corrIntrComboBox->addItems(QString("1 min,2 min,5 min,10 min,15 min,30 min,1 hour,1 day").split(","));
242 int mm = _corrIntrComboBox->findText(settings.value("corrIntr").toString());
243 if (mm != -1) {
244 _corrIntrComboBox->setCurrentIndex(mm);
245 }
246 _corrPortLineEdit = new QLineEdit(settings.value("corrPort").toString());
247 _corrTimeSpinBox = new QSpinBox();
248 _corrTimeSpinBox->setMinimum(0);
249 _corrTimeSpinBox->setMaximum(60);
250 _corrTimeSpinBox->setSingleStep(1);
251 _corrTimeSpinBox->setSuffix(" sec");
252 _corrTimeSpinBox->setValue(settings.value("corrTime").toInt());
253
254 connect(_corrPathLineEdit, SIGNAL(textChanged(const QString &)),
255 this, SLOT(slotBncTextChanged()));
256
257 connect(_corrPortLineEdit, SIGNAL(textChanged(const QString &)),
258 this, SLOT(slotBncTextChanged()));
259
260 // Feed Engine Options
261 // -------------------
262 _outPortLineEdit = new QLineEdit(settings.value("outPort").toString());
263 _waitTimeSpinBox = new QSpinBox();
264 _waitTimeSpinBox->setMinimum(1);
265 _waitTimeSpinBox->setMaximum(30);
266 _waitTimeSpinBox->setSingleStep(1);
267 _waitTimeSpinBox->setSuffix(" sec");
268 _waitTimeSpinBox->setValue(settings.value("waitTime").toInt());
269 _binSamplSpinBox = new QSpinBox();
270 _binSamplSpinBox->setMinimum(0);
271 _binSamplSpinBox->setMaximum(60);
272 _binSamplSpinBox->setSingleStep(5);
273 _binSamplSpinBox->setValue(settings.value("binSampl").toInt());
274 _binSamplSpinBox->setSuffix(" sec");
275 _outFileLineEdit = new QLineEdit(settings.value("outFile").toString());
276 _outUPortLineEdit = new QLineEdit(settings.value("outUPort").toString());
277
278 connect(_outPortLineEdit, SIGNAL(textChanged(const QString &)),
279 this, SLOT(slotBncTextChanged()));
280
281 connect(_outFileLineEdit, SIGNAL(textChanged(const QString &)),
282 this, SLOT(slotBncTextChanged()));
283
284 // Serial Output Options
285 // ---------------------
286 _serialMountPointLineEdit = new QLineEdit(settings.value("serialMountPoint").toString());
287 _serialPortNameLineEdit = new QLineEdit(settings.value("serialPortName").toString());
288 _serialBaudRateComboBox = new QComboBox();
289 _serialBaudRateComboBox->addItems(QString("110,300,600,"
290 "1200,2400,4800,9600,19200,38400,57600,115200").split(","));
291 int kk = _serialBaudRateComboBox->findText(settings.value("serialBaudRate").toString());
292 if (kk != -1) {
293 _serialBaudRateComboBox->setCurrentIndex(kk);
294 }
295 _serialFlowControlComboBox = new QComboBox();
296 _serialFlowControlComboBox->addItems(QString("OFF,XONXOFF,HARDWARE").split(","));
297 kk = _serialFlowControlComboBox->findText(settings.value("serialFlowControl").toString());
298 if (kk != -1) {
299 _serialFlowControlComboBox->setCurrentIndex(kk);
300 }
301 _serialDataBitsComboBox = new QComboBox();
302 _serialDataBitsComboBox->addItems(QString("5,6,7,8").split(","));
303 kk = _serialDataBitsComboBox->findText(settings.value("serialDataBits").toString());
304 if (kk != -1) {
305 _serialDataBitsComboBox->setCurrentIndex(kk);
306 }
307 _serialParityComboBox = new QComboBox();
308 _serialParityComboBox->addItems(QString("NONE,ODD,EVEN,SPACE").split(","));
309 kk = _serialParityComboBox->findText(settings.value("serialParity").toString());
310 if (kk != -1) {
311 _serialParityComboBox->setCurrentIndex(kk);
312 }
313 _serialStopBitsComboBox = new QComboBox();
314 _serialStopBitsComboBox->addItems(QString("1,2").split(","));
315 kk = _serialStopBitsComboBox->findText(settings.value("serialStopBits").toString());
316 if (kk != -1) {
317 _serialStopBitsComboBox->setCurrentIndex(kk);
318 }
319 _serialAutoNMEAComboBox = new QComboBox();
320 _serialAutoNMEAComboBox->addItems(QString("Auto,Manual").split(","));
321 kk = _serialAutoNMEAComboBox->findText(settings.value("serialAutoNMEA").toString());
322 if (kk != -1) {
323 _serialAutoNMEAComboBox->setCurrentIndex(kk);
324 }
325 _serialFileNMEALineEdit = new QLineEdit(settings.value("serialFileNMEA").toString());
326 _serialHeightNMEALineEdit = new QLineEdit(settings.value("serialHeightNMEA").toString());
327
328 connect(_serialMountPointLineEdit, SIGNAL(textChanged(const QString &)),
329 this, SLOT(slotBncTextChanged()));
330
331 connect(_serialAutoNMEAComboBox, SIGNAL(currentIndexChanged(const QString &)),
332 this, SLOT(slotBncTextChanged()));
333
334 // Outages Options
335 // ---------------
336 _obsRateComboBox = new QComboBox();
337 _obsRateComboBox->setEditable(false);
338 _obsRateComboBox->addItems(QString(",0.1 Hz,0.2 Hz,0.5 Hz,1 Hz,5 Hz").split(","));
339 kk = _obsRateComboBox->findText(settings.value("obsRate").toString());
340 if (kk != -1) {
341 _obsRateComboBox->setCurrentIndex(kk);
342 }
343 _adviseFailSpinBox = new QSpinBox();
344 _adviseFailSpinBox->setMinimum(0);
345 _adviseFailSpinBox->setMaximum(60);
346 _adviseFailSpinBox->setSingleStep(1);
347 _adviseFailSpinBox->setSuffix(" min");
348 _adviseFailSpinBox->setValue(settings.value("adviseFail").toInt());
349 _adviseRecoSpinBox = new QSpinBox();
350 _adviseRecoSpinBox->setMinimum(0);
351 _adviseRecoSpinBox->setMaximum(60);
352 _adviseRecoSpinBox->setSingleStep(1);
353 _adviseRecoSpinBox->setSuffix(" min");
354 _adviseRecoSpinBox->setValue(settings.value("adviseReco").toInt());
355 _adviseScriptLineEdit = new QLineEdit(settings.value("adviseScript").toString());
356
357 connect(_obsRateComboBox, SIGNAL(currentIndexChanged(const QString &)),
358 this, SLOT(slotBncTextChanged()));
359
360 // Miscellaneous Options
361 // ---------------------
362 _miscMountLineEdit = new QLineEdit(settings.value("miscMount").toString());
363 _miscPortLineEdit = new QLineEdit(settings.value("miscPort").toString());
364 _perfIntrComboBox = new QComboBox();
365 _perfIntrComboBox->setEditable(false);
366 _perfIntrComboBox->addItems(QString(",2 sec, 10 sec,1 min,5 min,15 min,1 hour,6 hours,1 day").split(","));
367 int ll = _perfIntrComboBox->findText(settings.value("perfIntr").toString());
368 if (ll != -1) {
369 _perfIntrComboBox->setCurrentIndex(ll);
370 }
371 _scanRTCMCheckBox = new QCheckBox();
372 _scanRTCMCheckBox->setCheckState(Qt::CheckState(
373 settings.value("scanRTCM").toInt()));
374
375 connect(_miscMountLineEdit, SIGNAL(textChanged(const QString &)),
376 this, SLOT(slotBncTextChanged()));
377
378 // Streams
379 // -------
380 _mountPointsTable = new QTableWidget(0,8);
381
382 _mountPointsTable->horizontalHeader()->resizeSection(1,34*ww);
383 _mountPointsTable->horizontalHeader()->resizeSection(2,9*ww);
384 _mountPointsTable->horizontalHeader()->resizeSection(3,7*ww);
385 _mountPointsTable->horizontalHeader()->resizeSection(4,7*ww);
386 _mountPointsTable->horizontalHeader()->resizeSection(5,5*ww);
387 _mountPointsTable->horizontalHeader()->resizeSection(6,5*ww);
388 _mountPointsTable->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
389 _mountPointsTable->horizontalHeader()->setStretchLastSection(true);
390 _mountPointsTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
391 _mountPointsTable->setHorizontalHeaderLabels(labels);
392 _mountPointsTable->setGridStyle(Qt::NoPen);
393 _mountPointsTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
394 _mountPointsTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
395 _mountPointsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
396 _mountPointsTable->hideColumn(0);
397 connect(_mountPointsTable, SIGNAL(itemSelectionChanged()),
398 SLOT(slotSelectionChanged()));
399 populateMountPointsTable();
400
401 _log = new QTextEdit();
402 _log->setReadOnly(true);
403 QFont msFont(""); msFont.setStyleHint(QFont::TypeWriter); // default monospace font
404 _log->setFont(msFont);
405 _log->document()->setMaximumBlockCount(1000);
406
407 // Combine Corrections
408 // -------------------
409 _cmbTable = new QTableWidget(0,3);
410 _cmbTable->setHorizontalHeaderLabels(QString("Mountpoint, AC Name, Weight").split(","));
411 _cmbTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
412 _cmbTable->setSelectionBehavior(QAbstractItemView::SelectRows);
413 _cmbTable->setMaximumWidth(30*ww);
414 _cmbTable->horizontalHeader()->resizeSection(0,10*ww);
415 _cmbTable->horizontalHeader()->resizeSection(1,8*ww);
416 _cmbTable->horizontalHeader()->resizeSection(2,8*ww);
417 _cmbTable->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
418 _cmbTable->horizontalHeader()->setStretchLastSection(true);
419 _cmbTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
420
421 _cmbMaxresLineEdit = new QLineEdit(settings.value("cmbMaxres").toString());
422
423 _cmbSamplSpinBox = new QSpinBox;
424 _cmbSamplSpinBox->setMinimum(10);
425 _cmbSamplSpinBox->setMaximum(60);
426 _cmbSamplSpinBox->setSingleStep(10);
427 _cmbSamplSpinBox->setMaximumWidth(9*ww);
428 _cmbSamplSpinBox->setValue(settings.value("cmbSampl").toInt());
429 _cmbSamplSpinBox->setSuffix(" sec");
430
431 QPushButton* addCmbRowButton = new QPushButton("Add Row");
432 QPushButton* delCmbRowButton = new QPushButton("Delete");
433
434 connect(_cmbTable, SIGNAL(itemSelectionChanged()),
435 SLOT(slotBncTextChanged()));
436
437 _cmbMethodComboBox = new QComboBox();
438 _cmbMethodComboBox->setEditable(false);
439 _cmbMethodComboBox->addItems(QString("Filter,Single-Epoch").split(","));
440 int im = _cmbMethodComboBox->findText(settings.value("cmbMethod").toString());
441 if (im != -1) {
442 _cmbMethodComboBox->setCurrentIndex(im);
443 }
444
445 int iRow = _cmbTable->rowCount();
446 if (iRow > 0) {
447 enableWidget(true, _cmbMethodComboBox);
448 _cmbMaxresLineEdit->setStyleSheet("background-color: white");
449 _cmbMaxresLineEdit->setEnabled(true);
450 _cmbSamplSpinBox->setEnabled(true);
451 }
452 else {
453 enableWidget(false, _cmbMethodComboBox);
454 _cmbMaxresLineEdit->setStyleSheet("background-color: lightGray");
455 _cmbMaxresLineEdit->setEnabled(false);
456 _cmbSamplSpinBox->setEnabled(false);
457 }
458
459 // Upload Results
460 // -------------
461 _uploadTable = new QTableWidget(0,12);
462 _uploadTable->setHorizontalHeaderLabels(QString("Host, Port, Mount, Password, System, CoM, SP3 File, RNX File, PID, SID, IOD, bytes").split(","));
463 _uploadTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
464 _uploadTable->setSelectionBehavior(QAbstractItemView::SelectRows);
465 _uploadTable->horizontalHeader()->resizeSection( 0,13*ww);
466 _uploadTable->horizontalHeader()->resizeSection( 1, 5*ww);
467 _uploadTable->horizontalHeader()->resizeSection( 2, 6*ww);
468 _uploadTable->horizontalHeader()->resizeSection( 3, 8*ww);
469 _uploadTable->horizontalHeader()->resizeSection( 4,11*ww);
470 _uploadTable->horizontalHeader()->resizeSection( 5, 4*ww);
471 _uploadTable->horizontalHeader()->resizeSection( 6,15*ww);
472 _uploadTable->horizontalHeader()->resizeSection( 7,15*ww);
473 _uploadTable->horizontalHeader()->resizeSection( 8, 4*ww);
474 _uploadTable->horizontalHeader()->resizeSection( 9, 4*ww);
475 _uploadTable->horizontalHeader()->resizeSection(10, 4*ww);
476 _uploadTable->horizontalHeader()->resizeSection(11,12*ww);
477 _uploadTable->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
478 _uploadTable->horizontalHeader()->setStretchLastSection(true);
479 _uploadTable->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
480
481 connect(_uploadTable, SIGNAL(itemSelectionChanged()),
482 SLOT(slotBncTextChanged()));
483
484 QPushButton* addUploadRowButton = new QPushButton("Add Row");
485 QPushButton* delUploadRowButton = new QPushButton("Del Row");
486 QPushButton* setUploadTrafoButton = new QPushButton("Custom Trafo");
487 _uploadIntrComboBox = new QComboBox;
488 _uploadIntrComboBox->setEditable(false);
489 _uploadIntrComboBox->addItems(QString("1 day,1 hour, 30 min,15 min,10 min,5 min,2 min,1 min").split(","));
490 ii = _uploadIntrComboBox->findText(settings.value("uploadIntr").toString());
491 if (ii != -1) {
492 _uploadIntrComboBox->setCurrentIndex(ii);
493 }
494
495 _uploadSamplRtcmEphCorrSpinBox = new QSpinBox;
496 _uploadSamplRtcmEphCorrSpinBox->setMinimum(0);
497 _uploadSamplRtcmEphCorrSpinBox->setMaximum(60);
498 _uploadSamplRtcmEphCorrSpinBox->setSingleStep(5);
499 _uploadSamplRtcmEphCorrSpinBox->setMaximumWidth(9*ww);
500 _uploadSamplRtcmEphCorrSpinBox->setValue(settings.value("uploadSamplRtcmEphCorr").toInt());
501 _uploadSamplRtcmEphCorrSpinBox->setSuffix(" sec");
502
503 _uploadSamplSp3SpinBox = new QSpinBox;
504 _uploadSamplSp3SpinBox->setMinimum(0);
505 _uploadSamplSp3SpinBox->setMaximum(15);
506 _uploadSamplSp3SpinBox->setSingleStep(1);
507 _uploadSamplSp3SpinBox->setMaximumWidth(9*ww);
508 _uploadSamplSp3SpinBox->setValue(settings.value("uploadSamplSp3").toInt());
509 _uploadSamplSp3SpinBox->setSuffix(" min");
510
511 _uploadSamplClkRnxSpinBox = new QSpinBox;
512 _uploadSamplClkRnxSpinBox->setMinimum(0);
513 _uploadSamplClkRnxSpinBox->setMaximum(60);
514 _uploadSamplClkRnxSpinBox->setSingleStep(5);
515 _uploadSamplClkRnxSpinBox->setMaximumWidth(9*ww);
516 _uploadSamplClkRnxSpinBox->setValue(settings.value("uploadSamplClkRnx").toInt());
517 _uploadSamplClkRnxSpinBox->setSuffix(" sec");
518
519 int iRowT = _uploadTable->rowCount();
520 if (iRowT > 0) {
521 enableWidget(true, _uploadIntrComboBox);
522 enableWidget(true, _uploadSamplRtcmEphCorrSpinBox);
523 enableWidget(true, _uploadSamplSp3SpinBox);
524 enableWidget(true, _uploadSamplClkRnxSpinBox);
525 }
526 else {
527 enableWidget(false, _uploadIntrComboBox);
528 enableWidget(false, _uploadSamplRtcmEphCorrSpinBox);
529 enableWidget(false, _uploadSamplSp3SpinBox);
530 enableWidget(false, _uploadSamplClkRnxSpinBox);
531 }
532
533 // Upload RTCM3 Ephemeris
534 // ----------------------
535 _uploadEphHostLineEdit = new QLineEdit(settings.value("uploadEphHost").toString());
536 _uploadEphPortLineEdit = new QLineEdit(settings.value("uploadEphPort").toString());
537 _uploadEphPasswordLineEdit = new QLineEdit(settings.value("uploadEphPassword").toString());
538 _uploadEphPasswordLineEdit->setEchoMode(QLineEdit::PasswordEchoOnEdit);
539 _uploadEphMountpointLineEdit = new QLineEdit(settings.value("uploadEphMountpoint").toString());
540 _uploadEphSampleSpinBox = new QSpinBox;
541 _uploadEphSampleSpinBox->setMinimum(5);
542 _uploadEphSampleSpinBox->setMaximum(60);
543 _uploadEphSampleSpinBox->setSingleStep(5);
544 _uploadEphSampleSpinBox->setMaximumWidth(9*ww);
545 _uploadEphSampleSpinBox->setValue(settings.value("uploadEphSample").toInt());
546 _uploadEphSampleSpinBox->setSuffix(" sec");
547 _uploadEphBytesCounter = new bncBytesCounter;
548
549 // Canvas with Editable Fields
550 // ---------------------------
551 _canvas = new QWidget;
552 setCentralWidget(_canvas);
553
554 _aogroup = new QTabWidget();
555 QWidget* pgroup = new QWidget();
556 QWidget* ggroup = new QWidget();
557 QWidget* sgroup = new QWidget();
558 QWidget* egroup = new QWidget();
559 QWidget* agroup = new QWidget();
560 QWidget* cgroup = new QWidget();
561 QWidget* ogroup = new QWidget();
562 QWidget* rgroup = new QWidget();
563 QWidget* sergroup = new QWidget();
564 QWidget* pppGroup1 = new QWidget();
565 QWidget* pppGroup2 = new QWidget();
566 QWidget* pppGroup3 = new QWidget();
567 QWidget* pppGroup4 = new QWidget();
568 QWidget* reqcgroup = new QWidget();
569 QWidget* cmbgroup = new QWidget();
570 QWidget* uploadgroup = new QWidget();
571 QWidget* uploadEphgroup = new QWidget();
572 _aogroup->addTab(pgroup,tr("Network"));
573 _aogroup->addTab(ggroup,tr("General"));
574 _aogroup->addTab(ogroup,tr("RINEX Observations"));
575 _aogroup->addTab(egroup,tr("RINEX Ephemeris"));
576 _aogroup->addTab(reqcgroup,tr("RINEX Editing && QC"));
577 _aogroup->addTab(cgroup,tr("Broadcast Corrections"));
578 _aogroup->addTab(sgroup,tr("Feed Engine"));
579 _aogroup->addTab(sergroup,tr("Serial Output"));
580 _aogroup->addTab(agroup,tr("Outages"));
581 _aogroup->addTab(rgroup,tr("Miscellaneous"));
582 _aogroup->addTab(pppGroup1,tr("PPP (1)"));
583 _aogroup->addTab(pppGroup2,tr("PPP (2)"));
584 _aogroup->addTab(pppGroup3,tr("PPP (3)"));
585 _aogroup->addTab(pppGroup4,tr("PPP (4)"));
586
587#ifdef USE_COMBINATION
588 _aogroup->addTab(cmbgroup,tr("Combine Corrections"));
589#endif
590 _aogroup->addTab(uploadgroup,tr("Upload Corrections"));
591 _aogroup->addTab(uploadEphgroup,tr("Upload Ephemeris"));
592
593 // Log Tab
594 // -------
595 _loggroup = new QTabWidget();
596 _loggroup->addTab(_log,tr("Log"));
597 _loggroup->addTab(_bncFigure,tr("Throughput"));
598 _loggroup->addTab(_bncFigureLate,tr("Latency"));
599 _loggroup->addTab(_bncFigurePPP,tr("PPP Plot"));
600
601 // Netowork (Proxy and SSL) Tab
602 // ----------------------------
603 QGridLayout* pLayout = new QGridLayout;
604 pLayout->setColumnMinimumWidth(0,13*ww);
605 _proxyPortLineEdit->setMaximumWidth(9*ww);
606
607 pLayout->addWidget(new QLabel("Settings for proxy in protected networks and for SSL authorization, leave boxes blank if none."),0, 0, 1, 50);
608 pLayout->addWidget(new QLabel("Proxy host"), 1, 0);
609 pLayout->addWidget(_proxyHostLineEdit, 1, 1, 1,10);
610 pLayout->addWidget(new QLabel("Proxy port"), 2, 0);
611 pLayout->addWidget(_proxyPortLineEdit, 2, 1);
612 pLayout->addWidget(new QLabel("Path to SSL Certificates"), 3, 0);
613 pLayout->addWidget(_sslCaCertPathLineEdit, 3, 1, 1,10);
614 pLayout->addWidget(new QLabel("Default: " + bncSslConfig::defaultPath()), 3,11, 1,20);
615 pLayout->addWidget(new QLabel("Ignore SSL Authorization Errors"), 4, 0);
616 pLayout->addWidget(_ignoreSslErrorsCheckBox, 4, 1, 1,10);
617 pLayout->addWidget(new QLabel(" "), 4, 0);
618 pLayout->addWidget(new QLabel(" "), 5, 0);
619 pLayout->addWidget(new QLabel(" "), 6, 0);
620 pgroup->setLayout(pLayout);
621
622 // General Tab
623 // -----------
624 QGridLayout* gLayout = new QGridLayout;
625 gLayout->setColumnMinimumWidth(0,14*ww);
626 _onTheFlyComboBox->setMaximumWidth(9*ww);
627
628 gLayout->addWidget(new QLabel("General settings for logfile, file handling, configuration on-the-fly, and auto-start."),0, 0, 1, 50);
629 gLayout->addWidget(new QLabel("Logfile (full path)"), 1, 0);
630 gLayout->addWidget(_logFileLineEdit, 1, 1, 1,30);
631 gLayout->addWidget(new QLabel("Append files"), 2, 0);
632 gLayout->addWidget(_rnxAppendCheckBox, 2, 1);
633 gLayout->addWidget(new QLabel("Reread configuration"), 3, 0);
634 gLayout->addWidget(_onTheFlyComboBox, 3, 1);
635 gLayout->addWidget(new QLabel("Auto start"), 4, 0);
636 gLayout->addWidget(_autoStartCheckBox, 4, 1);
637 gLayout->addWidget(new QLabel("Raw output file (full path)"), 5, 0);
638 gLayout->addWidget(_rawOutFileLineEdit, 5, 1, 1,30);
639 gLayout->addWidget(new QLabel(" "), 6, 0);
640 ggroup->setLayout(gLayout);
641
642 // RINEX Observations
643 // ------------------
644 QGridLayout* oLayout = new QGridLayout;
645 oLayout->setColumnMinimumWidth(0,14*ww);
646 _rnxIntrComboBox->setMaximumWidth(9*ww);
647 _rnxSamplSpinBox->setMaximumWidth(9*ww);
648
649 oLayout->addWidget(new QLabel("Saving RINEX observation files."),0, 0, 1,50);
650 oLayout->addWidget(new QLabel("Directory"), 1, 0);
651 oLayout->addWidget(_rnxPathLineEdit, 1, 1, 1,24);
652 oLayout->addWidget(new QLabel("Interval"), 2, 0);
653 oLayout->addWidget(_rnxIntrComboBox, 2, 1);
654 oLayout->addWidget(new QLabel(" Sampling"), 2, 2, Qt::AlignRight);
655 oLayout->addWidget(_rnxSamplSpinBox, 2, 3, Qt::AlignLeft);
656 oLayout->addWidget(new QLabel("Skeleton extension"), 3, 0);
657 oLayout->addWidget(_rnxSkelLineEdit, 3, 1, 1, 1, Qt::AlignLeft);
658 oLayout->addWidget(new QLabel("Script (full path)"), 4, 0);
659 oLayout->addWidget(_rnxScrpLineEdit, 4, 1, 1,24);
660 oLayout->addWidget(new QLabel("Version 3"), 5, 0);
661 oLayout->addWidget(_rnxV3CheckBox, 5, 1);
662 oLayout->addWidget(new QLabel(" "), 6, 0);
663 ogroup->setLayout(oLayout);
664
665 // RINEX Ephemeris
666 // ---------------
667 QGridLayout* eLayout = new QGridLayout;
668 eLayout->setColumnMinimumWidth(0,14*ww);
669 _ephIntrComboBox->setMaximumWidth(9*ww);
670 _outEphPortLineEdit->setMaximumWidth(9*ww);
671
672 eLayout->addWidget(new QLabel("Saving RINEX ephemeris files and ephemeris output through IP port."),0,0,1,50);
673 eLayout->addWidget(new QLabel("Directory"), 1, 0);
674 eLayout->addWidget(_ephPathLineEdit, 1, 1, 1,30);
675 eLayout->addWidget(new QLabel("Interval"), 2, 0);
676 eLayout->addWidget(_ephIntrComboBox, 2, 1);
677 eLayout->addWidget(new QLabel("Port"), 3, 0);
678 eLayout->addWidget(_outEphPortLineEdit, 3, 1);
679 eLayout->addWidget(new QLabel("Version 3"), 4, 0);
680 eLayout->addWidget(_ephV3CheckBox, 4, 1);
681 eLayout->addWidget(new QLabel(" "), 5, 0);
682 eLayout->addWidget(new QLabel(" "), 6, 0);
683 egroup->setLayout(eLayout);
684
685
686 // Broadcast Corrections
687 // ---------------------
688 QGridLayout* cLayout = new QGridLayout;
689 cLayout->setColumnMinimumWidth(0,14*ww);
690 _corrIntrComboBox->setMaximumWidth(9*ww);
691 _corrPortLineEdit->setMaximumWidth(9*ww);
692 _corrTimeSpinBox->setMaximumWidth(9*ww);
693
694 cLayout->addWidget(new QLabel("Saving Broadcast Ephemeris correction files and correction output through IP port."),0,0,1,50);
695 cLayout->addWidget(new QLabel("Directory, ASCII"), 1, 0);
696 cLayout->addWidget(_corrPathLineEdit, 1, 1, 1,20);
697 cLayout->addWidget(new QLabel("Interval"), 2, 0);
698 cLayout->addWidget(_corrIntrComboBox, 2, 1);
699 cLayout->addWidget(new QLabel("Port"), 3, 0);
700 cLayout->addWidget(_corrPortLineEdit, 3, 1);
701 cLayout->addWidget(new QLabel(" Wait for full corr epoch"), 3, 2, Qt::AlignRight);
702 cLayout->addWidget(_corrTimeSpinBox, 3, 3, Qt::AlignLeft);
703 cLayout->addWidget(new QLabel(" "), 4, 0);
704 cLayout->addWidget(new QLabel(" "), 5, 0);
705 cLayout->addWidget(new QLabel(" "), 6, 0);
706 cgroup->setLayout(cLayout);
707
708 // Feed Engine
709 // -----------
710 QGridLayout* sLayout = new QGridLayout;
711 sLayout->setColumnMinimumWidth(0,14*ww);
712 _outPortLineEdit->setMaximumWidth(9*ww);
713 _waitTimeSpinBox->setMaximumWidth(9*ww);
714 _binSamplSpinBox->setMaximumWidth(9*ww);
715 _outUPortLineEdit->setMaximumWidth(9*ww);
716
717 sLayout->addWidget(new QLabel("Output decoded observations in ASCII format to feed a real-time GNSS network engine."),0,0,1,50);
718 sLayout->addWidget(new QLabel("Port"), 1, 0);
719 sLayout->addWidget(_outPortLineEdit, 1, 1);
720 sLayout->addWidget(new QLabel("Wait for full obs epoch"), 1, 2, Qt::AlignRight);
721 sLayout->addWidget(_waitTimeSpinBox, 1, 3, Qt::AlignLeft);
722 sLayout->addWidget(new QLabel("Sampling"), 2, 0);
723 sLayout->addWidget(_binSamplSpinBox, 2, 1, Qt::AlignLeft);
724 sLayout->addWidget(new QLabel("File (full path)"), 3, 0);
725 sLayout->addWidget(_outFileLineEdit, 3, 1, 1, 20);
726 sLayout->addWidget(new QLabel("Port (unsynchronized)"), 4, 0);
727 sLayout->addWidget(_outUPortLineEdit, 4, 1);
728 sLayout->addWidget(new QLabel(" "), 5, 0);
729 sLayout->addWidget(new QLabel(" "), 6, 0);
730 sgroup->setLayout(sLayout);
731
732 // Serial Output
733 // -------------
734 QGridLayout* serLayout = new QGridLayout;
735 serLayout->setColumnMinimumWidth(0,14*ww);
736 _serialBaudRateComboBox->setMaximumWidth(9*ww);
737 _serialFlowControlComboBox->setMaximumWidth(11*ww);
738 _serialDataBitsComboBox->setMaximumWidth(5*ww);
739 _serialParityComboBox->setMaximumWidth(9*ww);
740 _serialStopBitsComboBox->setMaximumWidth(5*ww);
741 _serialAutoNMEAComboBox->setMaximumWidth(9*ww);
742 _serialHeightNMEALineEdit->setMaximumWidth(8*ww);
743
744 serLayout->addWidget(new QLabel("Port settings to feed a serial connected receiver."),0,0,1,30);
745 serLayout->addWidget(new QLabel("Mountpoint"), 1, 0, Qt::AlignLeft);
746 serLayout->addWidget(_serialMountPointLineEdit, 1, 1, 1, 2);
747 serLayout->addWidget(new QLabel("Port name"), 2, 0, Qt::AlignLeft);
748 serLayout->addWidget(_serialPortNameLineEdit, 2, 1, 1, 2);
749 serLayout->addWidget(new QLabel("Baud rate"), 3, 0, Qt::AlignLeft);
750 serLayout->addWidget(_serialBaudRateComboBox, 3, 1);
751 serLayout->addWidget(new QLabel("Flow control"), 3, 2, Qt::AlignRight);
752 serLayout->addWidget(_serialFlowControlComboBox, 3, 3);
753 serLayout->addWidget(new QLabel("Data bits"), 4, 0, Qt::AlignLeft);
754 serLayout->addWidget(_serialDataBitsComboBox, 4, 1);
755 serLayout->addWidget(new QLabel("Parity"), 4, 2, Qt::AlignRight);
756 serLayout->addWidget(_serialParityComboBox, 4, 3);
757 serLayout->addWidget(new QLabel(" Stop bits"), 4, 4, Qt::AlignRight);
758 serLayout->addWidget(_serialStopBitsComboBox, 4, 5);
759 serLayout->addWidget(new QLabel("NMEA"), 5, 0);
760 serLayout->addWidget(_serialAutoNMEAComboBox, 5, 1);
761 serLayout->addWidget(new QLabel(" File (full path)"), 5, 2, Qt::AlignRight);
762 serLayout->addWidget(_serialFileNMEALineEdit, 5, 3, 1,15);
763 serLayout->addWidget(new QLabel("Height"), 5,20, Qt::AlignRight);
764 serLayout->addWidget(_serialHeightNMEALineEdit, 5,21, 1,11);
765 serLayout->addWidget(new QLabel(" "), 6, 0);
766
767 sergroup->setLayout(serLayout);
768
769 // Outages
770 // -------
771 QGridLayout* aLayout = new QGridLayout;
772 aLayout->setColumnMinimumWidth(0,14*ww);
773 _obsRateComboBox->setMaximumWidth(9*ww);
774 _adviseFailSpinBox->setMaximumWidth(9*ww);
775 _adviseRecoSpinBox->setMaximumWidth(9*ww);
776
777 aLayout->addWidget(new QLabel("Failure and recovery reports, advisory notes."),0,0,1,50,Qt::AlignLeft);
778 aLayout->addWidget(new QLabel("Observation rate"), 1, 0);
779 aLayout->addWidget(_obsRateComboBox, 1, 1);
780 aLayout->addWidget(new QLabel("Failure threshold"), 2, 0);
781 aLayout->addWidget(_adviseFailSpinBox, 2, 1);
782 aLayout->addWidget(new QLabel("Recovery threshold"), 3, 0);
783 aLayout->addWidget(_adviseRecoSpinBox, 3, 1);
784 aLayout->addWidget(new QLabel("Script (full path)"), 4, 0);
785 aLayout->addWidget(_adviseScriptLineEdit, 4, 1, 1,30);
786 aLayout->addWidget(new QLabel(" "), 5, 0);
787 aLayout->addWidget(new QLabel(" "), 6, 0);
788 agroup->setLayout(aLayout);
789
790 // Miscellaneous
791 // -------------
792 QGridLayout* rLayout = new QGridLayout;
793 rLayout->setColumnMinimumWidth(0,14*ww);
794 _perfIntrComboBox->setMaximumWidth(9*ww);
795 _miscPortLineEdit->setMaximumWidth(9*ww);
796
797 rLayout->addWidget(new QLabel("Log latencies or scan RTCM streams for message types and antenna information or output raw data through TCP/IP port."),0, 0,1,30);
798 rLayout->addWidget(new QLabel("Mountpoint"), 1, 0);
799 rLayout->addWidget(_miscMountLineEdit, 1, 1, 1,7);
800 rLayout->addWidget(new QLabel("Log latency"), 2, 0);
801 rLayout->addWidget(_perfIntrComboBox, 2, 1);
802 rLayout->addWidget(new QLabel("Scan RTCM"), 3, 0);
803 rLayout->addWidget(_scanRTCMCheckBox, 3, 1);
804 rLayout->addWidget(new QLabel("Port"), 4, 0);
805 rLayout->addWidget(_miscPortLineEdit, 4, 1);
806 rLayout->addWidget(new QLabel(" "), 5, 0);
807 rLayout->addWidget(new QLabel(" "), 6, 0);
808 rgroup->setLayout(rLayout);
809
810 // PPP
811 // ---
812 QGridLayout* pppLayout1 = new QGridLayout();
813 int ir = 0;
814 pppLayout1->addWidget(new QLabel("<b>Precise Point Positioning (Input and Output)</b>"), ir, 0, 1, 7, Qt::AlignLeft);
815 ++ir;
816 pppLayout1->addWidget(new QLabel("Data Source"), ir, 0, Qt::AlignLeft);
817 pppLayout1->addWidget(_pppWidgets._dataSource, ir, 1);
818 pppLayout1->addItem(new QSpacerItem(4*ww, 0), ir, 2);
819 pppLayout1->addWidget(new QLabel("RINEX Observations"), ir, 3, Qt::AlignLeft);
820 pppLayout1->addWidget(_pppWidgets._rinexObs, ir, 4, 1, 2);
821 ++ir;
822 pppLayout1->addWidget(new QLabel("RINEX Orbits"), ir, 3, Qt::AlignLeft);
823 pppLayout1->addWidget(_pppWidgets._rinexNav, ir, 4, 1, 2);
824 ++ir;
825 pppLayout1->addWidget(new QLabel("Corrections"), ir, 0, Qt::AlignLeft);
826 pppLayout1->addWidget(_pppWidgets._corrMount, ir, 1);
827 pppLayout1->addWidget(new QLabel("Corrections"), ir, 3, Qt::AlignLeft);
828 pppLayout1->addWidget(_pppWidgets._corrFile, ir, 4, 1, 2);
829 ++ir;
830 pppLayout1->addWidget(new QLabel("<b>Input</b>"), ir, 0, Qt::AlignLeft);
831 pppLayout1->addWidget(new QLabel("<b>Output</b>"), ir, 4, 1, 2, Qt::AlignLeft);
832 ++ir;
833 pppLayout1->addWidget(new QLabel("Coordinates"), ir, 0, Qt::AlignLeft);
834 pppLayout1->addWidget(_pppWidgets._crdFile, ir, 1, 1, 2);
835 pppLayout1->addWidget(new QLabel("Log File"), ir, 4, Qt::AlignLeft);
836 pppLayout1->addWidget(_pppWidgets._logFile, ir, 5);
837 ++ir;
838 pppLayout1->addWidget(new QLabel("ANTEX"), ir, 0, Qt::AlignLeft);
839 pppLayout1->addWidget(_pppWidgets._antexFile, ir, 1, 1, 2);
840 pppLayout1->addWidget(new QLabel("NMEA File"), ir, 4, Qt::AlignLeft);
841 pppLayout1->addWidget(_pppWidgets._nmeaFile, ir, 5);
842 pppLayout1->addWidget(new QLabel("Port"), ir, 6, Qt::AlignLeft);
843 pppLayout1->addWidget(_pppWidgets._nmeaPort, ir, 7); _pppWidgets._nmeaPort->setMaximumWidth(8*ww);
844
845 pppLayout1->addItem(new QSpacerItem(4*ww, 0), ir, 8);
846
847 pppGroup1->setLayout(pppLayout1);
848
849 QVBoxLayout* pppLayout2 = new QVBoxLayout();
850 pppLayout2->addWidget(new QLabel("<b>Precise Point Positioning (Processed Stations)</b>"));
851 pppLayout2->addWidget(_pppWidgets._staTable, 99);
852 QHBoxLayout* pppLayout2sub = new QHBoxLayout();
853 pppLayout2sub->addWidget(_pppWidgets._addStaButton);
854 pppLayout2sub->addWidget(_pppWidgets._delStaButton);
855 pppLayout2sub->addStretch(99);
856
857 pppLayout2->addLayout(pppLayout2sub);
858
859 pppGroup2->setLayout(pppLayout2);
860
861 QGridLayout* pppLayout3 = new QGridLayout();
862 ir = 0;
863 pppLayout3->addWidget(new QLabel("<b>Precise Point Positioning (Options)</b>"), ir, 0, 1, 2, Qt::AlignLeft);
864 ++ir;
865 pppLayout3->addWidget(new QLabel("GPS LCs"), ir, 0, Qt::AlignLeft);
866 pppLayout3->addWidget(_pppWidgets._lcGPS, ir, 1);
867 pppLayout3->addItem(new QSpacerItem(8*ww, 0), ir, 2);
868 pppLayout3->addWidget(new QLabel("Sigma C1"), ir, 3, Qt::AlignLeft);
869 pppLayout3->addWidget(_pppWidgets._sigmaC1, ir, 4); _pppWidgets._sigmaC1->setMaximumWidth(8*ww);
870 pppLayout3->addItem(new QSpacerItem(8*ww, 0), ir, 5);
871 pppLayout3->addWidget(new QLabel("Sigma L1"), ir, 6, Qt::AlignLeft);
872 pppLayout3->addWidget(_pppWidgets._sigmaL1, ir, 7); _pppWidgets._sigmaL1->setMaximumWidth(8*ww);
873 ++ir;
874 pppLayout3->addWidget(new QLabel("GLONASS LCs"), ir, 0, Qt::AlignLeft);
875 pppLayout3->addWidget(_pppWidgets._lcGLONASS, ir, 1);
876 pppLayout3->addWidget(new QLabel("Max Res C1"), ir, 3, Qt::AlignLeft);
877 pppLayout3->addWidget(_pppWidgets._maxResC1, ir, 4); _pppWidgets._maxResC1->setMaximumWidth(8*ww);
878 pppLayout3->addWidget(new QLabel("Max Res L1"), ir, 6, Qt::AlignLeft);
879 pppLayout3->addWidget(_pppWidgets._maxResL1, ir, 7); _pppWidgets._maxResL1->setMaximumWidth(8*ww);
880 ++ir;
881 pppLayout3->addWidget(new QLabel("Galileo LCs"), ir, 0, Qt::AlignLeft);
882 pppLayout3->addWidget(_pppWidgets._lcGalileo, ir, 1);
883 pppLayout3->addWidget(new QLabel("Ele Wgt Code"), ir, 3, Qt::AlignLeft);
884 pppLayout3->addWidget(_pppWidgets._eleWgtCode, ir, 4);
885 pppLayout3->addWidget(new QLabel("Ele Wgt Phase"), ir, 6, Qt::AlignLeft);
886 pppLayout3->addWidget(_pppWidgets._eleWgtPhase, ir, 7);
887 ++ir;
888 pppLayout3->addWidget(new QLabel("Wait for corrections"), ir, 0, Qt::AlignLeft);
889 pppLayout3->addWidget(_pppWidgets._corrWaitTime, ir, 1);
890 pppLayout3->addWidget(new QLabel("Min # of Obs"), ir, 3, Qt::AlignLeft);
891 pppLayout3->addWidget(_pppWidgets._minObs, ir, 4);
892 pppLayout3->addWidget(new QLabel("Min Elevation"), ir, 6, Qt::AlignLeft);
893 pppLayout3->addWidget(_pppWidgets._minEle, ir, 7);
894 ++ir;
895 pppLayout3->addWidget(new QLabel("Seeding (seconcs)"), ir, 0, Qt::AlignLeft);
896 pppLayout3->addWidget(_pppWidgets._seedingTime, ir, 1);
897 ++ir;
898 pppLayout3->addWidget(new QLabel(""), ir, 8);
899 pppLayout3->setColumnStretch(8, 999);
900
901 pppGroup3->setLayout(pppLayout3);
902
903 // ------------------------
904 QVBoxLayout* pppLayout4 = new QVBoxLayout;
905 pppLayout4->addWidget(new QLabel("<b>Precise Point Positioning (Plots)</b>"));
906 pppLayout4->addSpacing(ww);
907
908 QHBoxLayout* pppLayout4Hlp1 = new QHBoxLayout;
909 pppLayout4Hlp1->addWidget(new QLabel("PPP Station "));
910 _pppWidgets._plotCoordinates->setMaximumWidth(8*ww);
911 pppLayout4Hlp1->addWidget(_pppWidgets._plotCoordinates);
912 pppLayout4Hlp1->addWidget(new QLabel("Nort-East-Up Time Series"));
913 pppLayout4Hlp1->addStretch();
914 pppLayout4->addLayout(pppLayout4Hlp1);
915 pppLayout4->addSpacing(ww);
916
917 QHBoxLayout* pppLayout4Hlp2 = new QHBoxLayout;
918 pppLayout4Hlp2->addWidget(new QLabel("Track Plot "));
919 connect(_pppWidgets._mapWinButton, SIGNAL(clicked()), SLOT(slotMapPPP()));
920 pppLayout4Hlp2->addWidget(_pppWidgets._mapWinButton);
921
922 pppLayout4Hlp2->addSpacing(1*ww);
923
924 pppLayout4Hlp2->addWidget(new QLabel("Google"));
925 pppLayout4Hlp2->addWidget(_pppWidgets._useGoogleMap);
926
927 pppLayout4Hlp2->addWidget(new QLabel("OSM"));
928 pppLayout4Hlp2->addWidget(_pppWidgets._useOpenStreetMap);
929
930 pppLayout4Hlp2->addSpacing(3*ww);
931
932 _pppWidgets._mapWinDotSize->setMaximumWidth(5*ww);
933 pppLayout4Hlp2->addWidget(_pppWidgets._mapWinDotSize);
934
935 pppLayout4Hlp2->addSpacing(3*ww);
936
937 pppLayout4Hlp2->addWidget(_pppWidgets._mapWinDotColor);
938
939 pppLayout4Hlp2->addSpacing(3*ww);
940
941 pppLayout4Hlp2->addWidget(new QLabel("Speed"));
942 pppLayout4Hlp2->addWidget(_pppWidgets._mapSpeedSlider);
943
944 pppLayout4Hlp2->addStretch();
945 pppLayout4->addLayout(pppLayout4Hlp2);
946
947 pppLayout4->addStretch();
948 pppGroup4->setLayout(pppLayout4);
949
950 // Reqc Processing
951 // ---------------
952 _reqcActionComboBox = new QComboBox();
953 _reqcActionComboBox->setEditable(false);
954 _reqcActionComboBox->addItems(QString(",Edit/Concatenate,Analyze").split(","));
955 int ik = _reqcActionComboBox->findText(settings.value("reqcAction").toString());
956 if (ik != -1) {
957 _reqcActionComboBox->setCurrentIndex(ik);
958 }
959 connect(_reqcActionComboBox, SIGNAL(currentIndexChanged(const QString &)),
960 this, SLOT(slotBncTextChanged()));
961
962 QGridLayout* reqcLayout = new QGridLayout;
963 _reqcActionComboBox->setMinimumWidth(15*ww);
964 _reqcActionComboBox->setMaximumWidth(15*ww);
965
966 _reqcObsFileChooser = new qtFileChooser(0, qtFileChooser::Files);
967 _reqcObsFileChooser->setFileName(settings.value("reqcObsFile").toString());
968 _reqcObsFileChooser->setWhatsThis(tr("Specify the full path to an observation file in RINEX v2 or v3 format."));
969 _reqcObsFileChooser->setMinimumWidth(15*ww);
970 _reqcObsFileChooser->setMaximumWidth(15*ww);
971
972 _reqcNavFileChooser = new qtFileChooser(0, qtFileChooser::Files);
973 _reqcNavFileChooser->setFileName(settings.value("reqcNavFile").toString());
974 _reqcNavFileChooser->setWhatsThis(tr("Specify the full path to a RINEX v2 or v3 navigation file."));
975 _reqcNavFileChooser->setMinimumWidth(15*ww);
976 _reqcNavFileChooser->setMaximumWidth(15*ww);
977
978 _reqcOutObsLineEdit = new QLineEdit(settings.value("reqcOutObsFile").toString());
979 _reqcOutObsLineEdit->setWhatsThis(tr("Specify the full path to a RINEX observation output file."));
980 _reqcOutObsLineEdit->setMinimumWidth(15*ww);
981 _reqcOutObsLineEdit->setMaximumWidth(15*ww);
982
983 _reqcOutNavLineEdit = new QLineEdit(settings.value("reqcOutNavFile").toString());
984 _reqcOutNavLineEdit->setWhatsThis(tr("Specify the full path to a RINEX navigation output file."));
985 _reqcOutNavLineEdit->setMinimumWidth(15*ww);
986 _reqcOutNavLineEdit->setMaximumWidth(15*ww);
987
988 _reqcOutLogLineEdit = new QLineEdit(settings.value("reqcOutLogFile").toString());
989 _reqcOutLogLineEdit->setWhatsThis(tr("Specify the full path to a logfile."));
990 _reqcOutLogLineEdit->setMinimumWidth(15*ww);
991 _reqcOutLogLineEdit->setMaximumWidth(15*ww);
992
993 _reqcPlotDirLineEdit = new QLineEdit(settings.value("reqcPlotDir").toString());
994 _reqcPlotDirLineEdit->setWhatsThis(tr("Specify the directory name for saving plots."));
995 _reqcPlotDirLineEdit->setMinimumWidth(15*ww);
996 _reqcPlotDirLineEdit->setMaximumWidth(15*ww);
997
998 _reqcSkyPlotSystems = new QComboBox();
999 _reqcSkyPlotSystems->setEditable(false);
1000 _reqcSkyPlotSystems->addItems(QString("ALL,GPS,GLONASS,Galileo").split(","));
1001 ik = _reqcSkyPlotSystems->findText(settings.value("reqcSkyPlotSystems").toString());
1002 if (ik != -1) {
1003 _reqcSkyPlotSystems->setCurrentIndex(ik);
1004 }
1005
1006 ir = 0;
1007 reqcLayout->addWidget(new QLabel("RINEX file editing, concatenation and quality check."),ir, 0, 1, 20);
1008 ++ir;
1009 reqcLayout->addWidget(new QLabel("Action"), ir, 0, Qt::AlignLeft);
1010 reqcLayout->addWidget(_reqcActionComboBox, ir, 1, Qt::AlignLeft);
1011 _reqcEditOptionButton = new QPushButton("Set Edit Options");
1012 reqcLayout->addWidget(_reqcEditOptionButton, ir, 3, Qt::AlignRight);
1013 ++ir;
1014 reqcLayout->addWidget(new QLabel("Input files (full path)"), ir, 0, Qt::AlignLeft);
1015 reqcLayout->addWidget(_reqcObsFileChooser, ir, 1, Qt::AlignRight);
1016 reqcLayout->addWidget(new QLabel("Obs"), ir, 2, Qt::AlignLeft);
1017 reqcLayout->addWidget(_reqcNavFileChooser, ir, 3, Qt::AlignRight);
1018 reqcLayout->addWidget(new QLabel("Nav"), ir, 4, Qt::AlignLeft);
1019 ++ir;
1020 reqcLayout->addWidget(new QLabel("Output files (full path)"), ir, 0, Qt::AlignLeft);
1021 reqcLayout->addWidget(_reqcOutObsLineEdit, ir, 1, Qt::AlignRight);
1022 reqcLayout->addWidget(new QLabel("Obs"), ir, 2, Qt::AlignLeft);
1023 reqcLayout->addWidget(_reqcOutNavLineEdit, ir, 3, Qt::AlignRight);
1024 reqcLayout->addWidget(new QLabel("Nav"), ir, 4, Qt::AlignLeft);
1025 ++ir;
1026 reqcLayout->addWidget(_reqcOutLogLineEdit, ir, 1, Qt::AlignRight);
1027 reqcLayout->addWidget(new QLabel("Log"), ir, 2, Qt::AlignLeft);
1028 ++ir;
1029 reqcLayout->addWidget(new QLabel("Directory for plots"), ir, 0, Qt::AlignLeft);
1030 reqcLayout->addWidget(_reqcPlotDirLineEdit, ir, 1, Qt::AlignRight);
1031 ++ir;
1032 reqcLayout->addWidget(new QLabel("Sky plots for"), ir, 0, Qt::AlignLeft);
1033 reqcLayout->addWidget(_reqcSkyPlotSystems, ir, 1, Qt::AlignRight);
1034 ++ir;
1035 reqcLayout->addWidget(new QLabel(""), ir, 1);
1036 reqcLayout->setRowStretch(ir, 999);
1037
1038 reqcLayout->setColumnMinimumWidth(2, 8*ww);
1039 reqcLayout->setColumnMinimumWidth(4, 8*ww);
1040
1041 reqcgroup->setLayout(reqcLayout);
1042
1043 connect(_reqcEditOptionButton, SIGNAL(clicked()),
1044 this, SLOT(slotReqcEditOption()));
1045
1046 // Combine Corrections
1047 // -------------------
1048 QGridLayout* cmbLayout = new QGridLayout;
1049
1050 populateCmbTable();
1051 cmbLayout->addWidget(_cmbTable, 0, 0, 6, 3);
1052 cmbLayout->addWidget(new QLabel(" "), 0, 5);
1053 cmbLayout->addWidget(new QLabel("Combine Broadcast Correction streams."), 0, 6, 1, 50);
1054 cmbLayout->addWidget(new QLabel(" "), 1, 5);
1055 cmbLayout->addWidget(addCmbRowButton, 1, 6);
1056 cmbLayout->addWidget(delCmbRowButton, 1, 7);
1057 cmbLayout->addWidget(new QLabel(" "), 2, 5);
1058 cmbLayout->addWidget(new QLabel("Method"), 2, 6, Qt::AlignRight);
1059 cmbLayout->addWidget(_cmbMethodComboBox, 2, 7, Qt::AlignRight);
1060 cmbLayout->addWidget(new QLabel(" "), 3, 5);
1061 cmbLayout->addWidget(new QLabel("Maximal Residuum"), 3, 6, Qt::AlignRight);
1062 cmbLayout->addWidget(_cmbMaxresLineEdit, 3, 7, Qt::AlignRight);
1063 cmbLayout->addWidget(new QLabel(" "), 4, 5);
1064 cmbLayout->addWidget(new QLabel("Sampling"), 4, 6, Qt::AlignRight);
1065 cmbLayout->addWidget(_cmbSamplSpinBox, 4, 7, Qt::AlignRight);
1066 cmbLayout->addWidget(new QLabel(" "), 5, 0);
1067
1068 connect(addCmbRowButton, SIGNAL(clicked()), this, SLOT(slotAddCmbRow()));
1069 connect(delCmbRowButton, SIGNAL(clicked()), this, SLOT(slotDelCmbRow()));
1070
1071 cmbgroup->setLayout(cmbLayout);
1072
1073 // Upload Layout (Clocks)
1074 // ----------------------
1075 QGridLayout* uploadHlpLayout = new QGridLayout();
1076
1077 connect(addUploadRowButton, SIGNAL(clicked()), this, SLOT(slotAddUploadRow()));
1078 connect(delUploadRowButton, SIGNAL(clicked()), this, SLOT(slotDelUploadRow()));
1079 connect(setUploadTrafoButton, SIGNAL(clicked()), this, SLOT(slotSetUploadTrafo()));
1080
1081 uploadHlpLayout->addWidget(addUploadRowButton, 0, 0);
1082 uploadHlpLayout->addWidget(delUploadRowButton, 0, 1);
1083 uploadHlpLayout->addWidget(new QLabel("Interval"), 0, 2, Qt::AlignRight);
1084 uploadHlpLayout->addWidget(_uploadIntrComboBox, 0, 3);
1085 uploadHlpLayout->addWidget(new QLabel(" Sampling: Orb"), 0, 4, Qt::AlignRight);
1086 uploadHlpLayout->addWidget(_uploadSamplRtcmEphCorrSpinBox, 0, 5);
1087 uploadHlpLayout->addWidget(new QLabel("SP3"), 0, 6, Qt::AlignRight);
1088 uploadHlpLayout->addWidget(_uploadSamplSp3SpinBox, 0, 7);
1089 uploadHlpLayout->addWidget(new QLabel("RNX"), 0, 8, Qt::AlignRight);
1090 uploadHlpLayout->addWidget(_uploadSamplClkRnxSpinBox, 0, 9);
1091 uploadHlpLayout->addWidget(setUploadTrafoButton, 0,10);
1092
1093 QBoxLayout* uploadLayout = new QBoxLayout(QBoxLayout::TopToBottom);
1094 populateUploadTable();
1095
1096 uploadLayout->addWidget(new QLabel("Upload RTCMv3 Broadcast Corrections to caster."));
1097 uploadLayout->addWidget(_uploadTable);
1098 uploadLayout->addLayout(uploadHlpLayout);
1099
1100 uploadgroup->setLayout(uploadLayout);
1101
1102 // Upload Layout (Ephemeris)
1103 // -------------------------
1104 QGridLayout* uploadLayoutEph = new QGridLayout;
1105
1106 uploadLayoutEph->setColumnMinimumWidth(0, 9*ww);
1107 _uploadEphPortLineEdit->setMaximumWidth(9*ww);
1108 _uploadEphPasswordLineEdit->setMaximumWidth(9*ww);
1109 _uploadEphMountpointLineEdit->setMaximumWidth(12*ww);
1110
1111 uploadLayoutEph->addWidget(new QLabel("Upload concatenated RTCMv3 Broadcast Ephemeris to caster."), 0, 0, 1, 50);
1112 uploadLayoutEph->addWidget(new QLabel("Host"), 1, 0);
1113 uploadLayoutEph->addWidget(_uploadEphHostLineEdit, 1, 1, 1, 3);
1114 uploadLayoutEph->addWidget(new QLabel(" Port"), 1, 4, Qt::AlignRight);
1115 uploadLayoutEph->addWidget(_uploadEphPortLineEdit, 1, 5, 1, 1);
1116 uploadLayoutEph->addWidget(new QLabel("Mountpoint "), 2, 0);
1117 uploadLayoutEph->addWidget(_uploadEphMountpointLineEdit, 2, 1);
1118 uploadLayoutEph->addWidget(new QLabel(" Password"), 2, 2, Qt::AlignRight);
1119 uploadLayoutEph->addWidget(_uploadEphPasswordLineEdit, 2, 3);
1120 uploadLayoutEph->addWidget(new QLabel("Sampling"), 3, 0);
1121 uploadLayoutEph->addWidget(_uploadEphSampleSpinBox, 3, 1);
1122 uploadLayoutEph->addWidget(new QLabel("Uploaded"), 4, 0);
1123 uploadLayoutEph->addWidget(_uploadEphBytesCounter, 4, 1);
1124 uploadLayoutEph->addWidget(new QLabel(" "), 5, 0);
1125 uploadLayoutEph->addWidget(new QLabel(" "), 6, 0);
1126
1127 uploadEphgroup->setLayout(uploadLayoutEph);
1128
1129 connect(_uploadEphHostLineEdit, SIGNAL(textChanged(const QString &)),
1130 this, SLOT(slotBncTextChanged()));
1131
1132 // Main Layout
1133 // -----------
1134 QGridLayout* mLayout = new QGridLayout;
1135 _aogroup->setCurrentIndex(settings.value("startTab").toInt());
1136 mLayout->addWidget(_aogroup, 0,0);
1137 mLayout->addWidget(_mountPointsTable, 1,0);
1138 _loggroup->setCurrentIndex(settings.value("statusTab").toInt());
1139 mLayout->addWidget(_loggroup, 2,0);
1140
1141 _canvas->setLayout(mLayout);
1142
1143 // WhatsThis
1144 // ---------
1145 _proxyHostLineEdit->setWhatsThis(tr("<p>If you are running BNC within a protected Local Area Network (LAN), you might need to use a proxy server to access the Internet. Enter your proxy server IP and port number in case one is operated in front of BNC. If you do not know the IP and port of your proxy server, check the proxy server settings in your Internet browser or ask your network administrator.</p><p>Note that IP streaming is sometimes not allowed in a LAN. In this case you need to ask your network administrator for an appropriate modification of the local security policy or for the installation of a TCP relay to the NTRIP broadcasters. If these are not possible, you might need to run BNC outside your LAN on a network that has unobstructed connection to the Internet.</p>"));
1146 _proxyPortLineEdit->setWhatsThis(tr("<p>Enter your proxy server port number in case a proxy is operated in front of BNC.</p>"));
1147 _sslCaCertPathLineEdit->setWhatsThis(tr("<p>Communication with an NTRIP broadcaster over SSL requires the exchange of client and/or server certificates. Specify the path to a directory where you save certificates on your system. Don't try communication via SSL if you are not sure wheter this is supported by the involved NTRIP broadcaster. Note that SSL communication is usually done over port 443.</p>"));
1148 _ignoreSslErrorsCheckBox->setWhatsThis(tr("<p>SSL communication may involve queries coming from the NTRIP broadcaster. Tick 'Ignore SSL authorization erros' if you don't want to be bothered with this.</p>"));
1149 _waitTimeSpinBox->setWhatsThis(tr("<p>When feeding a real-time GNSS network engine waiting for synchronized input epoch by epoch, BNC drops whatever is received later than 'Wait for full obs epoch' seconds. A value of 3 to 5 seconds is recommended, depending on the latency of the incoming streams and the delay acceptable to your real-time GNSS network engine or products.</p>"));
1150 _outFileLineEdit->setWhatsThis(tr("Specify the full path to a file where synchronized observations are saved in plain ASCII format. Beware that the size of this file can rapidly increase depending on the number of incoming streams."));
1151 _outPortLineEdit->setWhatsThis(tr("BNC can produce synchronized observations in a plain ASCII format on your local host through an IP port. Specify a port number here to activate this function."));
1152 _outUPortLineEdit->setWhatsThis(tr("BNC can produce unsynchronized observations in a plain ASCII format on your local host through an IP port. Specify a port number here to activate this function."));
1153 _outEphPortLineEdit->setWhatsThis(tr("BNC can produce ephemeris data in RINEX ASCII format on your local host through an IP port. Specify a port number here to activate this function."));
1154 _corrPortLineEdit->setWhatsThis(tr("BNC can produce Broadcast Ephemeris Corrections on your local host through an IP port. Specify a port number here to activate this function."));
1155 _corrTimeSpinBox->setWhatsThis(tr("<p>Concerning output through IP port, BNC drops Broadcast Ephemeris Corrections received later than 'Wait for full corr epoch' seconds. A value of 2 to 5 seconds is recommended, depending on the latency of the incoming correction stream(s) and the delay acceptable to your real-time application.</p><p>Specifying a value of '0' means that BNC immediately outputs all incoming Broadcast Epemeris Corrections and does not drop any of them for latency reasons.</p>"));
1156 _rnxPathLineEdit->setWhatsThis(tr("Here you specify the path to where the RINEX Observation files will be stored. If the specified directory does not exist, BNC will not create RINEX Observation files."));
1157 _ephPathLineEdit->setWhatsThis(tr("Specify the path for saving Broadcast Ephemeris data as RINEX Navigation files. If the specified directory does not exist, BNC will not create RINEX Navigation files."));
1158 _corrPathLineEdit->setWhatsThis(tr("Specify a directory for saving Broadcast Ephemeris Correction files. If the specified directory does not exist, BNC will not create the files."));
1159 _rnxScrpLineEdit->setWhatsThis(tr("<p>Whenever a RINEX Observation file is saved, you might want to compress, copy or upload it immediately via FTP. BNC allows you to execute a script/batch file to carry out these operations. To do that specify the full path of the script/batch file here. BNC will pass the full RINEX Observation file path to the script as a command line parameter (%1 on Windows systems, $1 onUnix/Linux systems).</p>"));
1160 _rnxSkelLineEdit->setWhatsThis(tr("<p>BNC allows using personal skeleton files that contain the header records you would like to include. You can derive a personal RINEX header skeleton file from the information given in an up to date sitelog.</p><p>A file in the RINEX Observations 'Directory' with a 'Skeleton extension' suffix is interpreted by BNC as a personal RINEX header skeleton file for the corresponding stream.</p>"));
1161 _rnxAppendCheckBox->setWhatsThis(tr("<p>When BNC is started, new files are created by default and any existing files with the same name will be overwritten. However, users might want to append already existing files following a restart of BNC, a system crash or when BNC crashed. Tick 'Append files' to continue with existing files and keep what has been recorded so far.</p>"));
1162 _autoStartCheckBox->setWhatsThis(tr("<p>Tick 'Auto start' for auto-start of BNC at startup time in window mode with preassigned processing options.</p>"));
1163 _rawOutFileLineEdit->setWhatsThis(tr("<p>Save all data coming in through various streams in the received order and format in one file.</p>"));
1164 _onTheFlyComboBox->setWhatsThis(tr("<p>When operating BNC online in 'no window' mode, some configuration parameters can be changed on-the-fly without interrupting the running process. For that BNC rereads parts of its configuration in pre-defined intervals.<p></p>Select '1 min', '5 min', '1 hour', or '1 day' to force BNC to reread its configuration every full minute, hour, or day and let in between edited configuration options become effective on-the-fly without terminating uninvolved threads.</p><p>Note that when operating BNC in window mode, on-the-fly changeable configuration options become effective immediately through 'Save & Reread Configuration'.</p>"));
1165 _rnxIntrComboBox->setWhatsThis(tr("<p>Select the length of the RINEX Observation file.</p>"));
1166 _ephIntrComboBox->setWhatsThis(tr("<p>Select the length of the RINEX Navigation file.</p>"));
1167 _corrIntrComboBox->setWhatsThis(tr("<p>Select the length of the Broadcast Ephemeris Correction files.</p>"));
1168 _rnxSamplSpinBox->setWhatsThis(tr("<p>Select the RINEX Observation sampling interval in seconds. A value of zero '0' tells BNC to store all received epochs into RINEX.</p>"));
1169 _binSamplSpinBox->setWhatsThis(tr("<p>Select the synchronized observation sampling interval in seconds. A value of zero '0' tells BNC to send/store all received epochs.</p>"));
1170 _obsRateComboBox->setWhatsThis(tr("<p>BNC can collect all returns (success or failure) coming from a decoder within a certain short time span to then decide whether a stream has an outage or its content is corrupted. The procedure needs a rough estimate of the expected 'Observation rate' of the incoming streams. When a continuous problem is detected, BNC can inform its operator about this event through an advisory note.</p>"));
1171 _adviseRecoSpinBox->setWhatsThis(tr("<p>Following a stream outage or a longer series of bad observations, an advisory note is generated when valid observations are received again throughout the 'Recovery threshold' time span. A value of about 5min (default) is recommended.</p><p>A value of zero '0' means that for any stream recovery, however short, BNC immediately generates an advisory note.</p>"));
1172 _adviseFailSpinBox->setWhatsThis(tr("<p>An advisory note is generated when no (or only corrupted) observations are seen throughout the 'Failure threshold' time span. A value of 15 min (default) is recommended.</p><p>A value of zero '0' means that for any stream failure, however short, BNC immediately generates an advisory note.</p>"));
1173 _logFileLineEdit->setWhatsThis(tr("<p>Records of BNC's activities are shown in the 'Log' tab on the bottom of this window. They can be saved into a file when a valid path is specified in the 'Logfile (full path)' field.</p><p>The logfile name will automatically be extended by a string '_YYMMDD' carrying the current date."));
1174 _adviseScriptLineEdit->setWhatsThis(tr("<p>Specify the full path to a script or batch file to handle advisory notes generated in the event of corrupted streams or stream outages. The affected mountpoint and one of the comments 'Begin_Outage', 'End_Outage', 'Begin_Corrupted', or 'End_Corrupted' are passed on to the script as command line parameters.</p><p>The script may have the task to send the advisory notes by email to BNC's operator and/or to the affected stream provider. An empty option field (default) or invalid path means that you don't want to use this option.</p>"));
1175 _perfIntrComboBox->setWhatsThis(tr("<p>BNC can average latencies per stream over a certain period of GPS time. The resulting mean latencies are recorded in the 'Log' tab at the end of each 'Log latency' interval together with results of a statistical evaluation (approximate number of covered epochs, data gaps).</p><p>Select a 'Log latency' interval or select the empty option field if you do not want BNC to log latencies and statistical information.</p>"));
1176 _mountPointsTable->setWhatsThis(tr("<p>Streams selected for retrieval are listed in the 'Streams' section. Clicking on 'Add Stream' button will open a window that allows the user to select data streams from an NTRIP broadcaster according to their mountpoints. To remove a stream from the 'Streams' list, highlight it by clicking on it and hit the 'Delete Stream' button. You can also remove multiple streams by highlighting them using +Shift and +Ctrl.</p><p>BNC automatically allocates one of its internal decoders to a stream based on the stream's 'format' as given in the sourcetable. BNC allows users to change this selection by editing the decoder string. Double click on the 'decoder' field, enter your preferred decoder and then hit Enter. The accepted decoder strings are 'RTCM_2.x', 'RTCM_3.x' and 'RTNET'.</p><p>In case you need to log the raw data as is, BNC allows users to by-pass its decoders and directly save the input in daily log files. To do this specify the decoder string as 'ZERO'.</p><p>BNC can also retrieve streams from virtual reference stations (VRS). VRS streams are indicated by a 'yes' in the 'nmea' column. To initiate these streams, the approximate latitude/longitude rover position is sent to the NTRIP broadcaster. The default values can be change according to your requirement. Double click on 'lat' and 'long' fields, enter the values you wish to send and then hit Enter.</p>"));
1177 _log->setWhatsThis(tr("Records of BNC's activities are shown in the 'Log' tab. The message log covers the communication status between BNC and the NTRIP broadcaster as well as any problems that occur in the communication link, stream availability, stream delay, stream conversion etc."));
1178 _bncFigure->setWhatsThis(tr("The bandwidth consumtion per stream is shown in the 'Throughput' tab in bits per second (bps) or kilo bits per second (kbps)."));
1179 _bncFigureLate->setWhatsThis(tr("The individual latency of observations in each incoming stream is shown in the 'Latency' tab. Streams not carrying observations (i.e. those providing only broadcast ephemeris messages) are not considered here. Note that the calculation of correct latencies requires the clock of the host computer to be properly synchronized."));
1180 _ephV3CheckBox->setWhatsThis(tr("The default format for output of RINEX Navigation data containing Broadcast Ephemeris is RINEX Version 2. Select 'Version 3' if you want to output the ephemeris in RINEX Version 3 format."));
1181 _rnxV3CheckBox->setWhatsThis(tr("The default format for RINEX Observation files is RINEX Version 2. Select 'Version 3' if you want to save the observations in RINEX Version 3 format."));
1182 _miscMountLineEdit->setWhatsThis(tr("<p>Specify a mountpoint to apply any of the options shown below. Enter 'ALL' if you want to apply these options to all configured streams.</p><p>An empty option field (default) means that you don't want BNC to apply any of these options.</p>"));
1183 _miscPortLineEdit->setWhatsThis(tr("BNC can output an incoming stream through a TCP/IP port of your local host. Specify a port number here to activate this function."));
1184 _scanRTCMCheckBox->setWhatsThis(tr("<p>Tick 'Scan RTCM' to log the numbers of incomming message types as well as contained antenna coordinates, antenna heigt, and antenna descriptor.</p><p>In case of RTCM Version 3 MSM streams, BNC will also log contained RINEX Version 3 observation types.</p>."));
1185 _serialMountPointLineEdit->setWhatsThis(tr("<p>Enter a 'Mountpoint' to forward the corresponding stream to a serial connected receiver.</p>"));
1186 _serialPortNameLineEdit->setWhatsThis(tr("<p>Enter the serial 'Port name' selected for communication with your serial connected receiver. Valid port names are</p><pre>Windows: COM1, COM2<br>Linux: /dev/ttyS0, /dev/ttyS1<br>FreeBSD: /dev/ttyd0, /dev/ttyd1<br>Digital Unix: /dev/tty01, /dev/tty02<br>HP-UX: /dev/tty1p0, /dev/tty2p0<br>SGI/IRIX: /dev/ttyf1, /dev/ttyf2<br>SunOS/Solaris: /dev/ttya, /dev/ttyb</pre><p>Note that you must plug a serial cable in the port defined here before you start BNC.</p>"));
1187 _serialBaudRateComboBox->setWhatsThis(tr("<p>Select a 'Baud rate' for the serial output link.</p><p>Note that your selection must equal the baud rate configured to the serial connected receiver. Note further that using a high baud rate is recommended.</p>"));
1188 _serialParityComboBox->setWhatsThis(tr("<p>Select the 'Parity' for the serial output link.</p><p>Note that your selection must equal the parity selection configured to the serial connected receiver. Note further that parity is often set to 'NONE'.</p>"));
1189 _serialDataBitsComboBox->setWhatsThis(tr("<p>Select the number of 'Data bits' for the serial output link.</p><p>Note that your selection must equal the number of data bits configured to the serial connected receiver. Note further that often 8 data bits are used.</p>"));
1190 _serialStopBitsComboBox->setWhatsThis(tr("<p>Select the number of 'Stop bits' for the serial output link.</p><p>Note that your selection must equal the number of stop bits configured to the serial connected receiver. Note further that often 1 stop bit is used.</p>"));
1191 _serialFlowControlComboBox->setWhatsThis(tr("<p>Select a 'Flow control' for the serial output link.</p><p>Note that your selection must equal the flow control configured to the serial connected receiver. Select 'OFF' if you don't know better.</p>"));
1192 _serialAutoNMEAComboBox->setWhatsThis(tr("<p>Select 'Auto' to automatically forward NMEA-GGA messages coming from your serial connected receiver to the NTRIP broadcaster and/or save them in a file.</p><p>Select 'Manual' only when handling a VRS stream and your serial connected receiver doesn't generate NMEA-GGA messages.</p>"));
1193 _serialFileNMEALineEdit->setWhatsThis(tr("<p>Specify the full path to a file where NMEA messages coming from your serial connected receiver are saved.</p>"));
1194 _serialHeightNMEALineEdit->setWhatsThis(tr("<p>Specify an approximate 'Height' above mean sea level in meter for your VRS to simulate an inital NMEA-GGA message.</p><p>The setting of this option is ignored in case of streams coming from physical reference stations.</p>"));
1195 _reqcActionComboBox->setWhatsThis(tr("<p>BNC allows to edit or concatenate RINEX v2 or v3 files or to perform a quality check following UNAVCO's famous 'teqc' program.</p>"));
1196 _reqcEditOptionButton->setWhatsThis(tr("<p>Specify options for editing RINEX v2 or v3 files.</p>"));
1197 _bncFigurePPP->setWhatsThis(tr("PPP time series of North (red), East (green) and Up (blue) coordinate components are shown in the 'PPP Plot' tab when the corresponting option is selected above. Values are either referred to an XYZ reference coordinate (if specified) or referred to the first estimated set of coordinate compoments. The sliding PPP time series window covers the period of the latest 5 minutes."));
1198 _cmbTable->setWhatsThis(tr("<p>BNC allows to process several orbit and clock corrections streams in real-time to produce, encode, upload and save a combination of correctors coming from various providers. Hit the 'Add Row' button, double click on the 'Mountpoint' field to enter a Broadcast Ephemeris corrections mountpoint from the 'Streams' section below and hit Enter. Then double click on the 'AC Name' field to enter your choice of an abbreviation for the Analysis Center (AC) providing the stream. Finally, double click on the 'Weight' field to enter the weight to be applied for this stream in the combination.<ul><li>Note that an appropriate 'Wait for full corr epoch' value needs to be specified for the combination under the 'Broadcast Corrections' tab. A value of 15 seconds would make sense there if the update rate of incoming clock corrections is i.e. 10 seconds.</li><li>Note also that you need to tick 'Use GLONASS' which is part ot the 'PPP (2)' panel in case you want to produce an GPS plus GLONASS combination.</li></ul></p><p>Note further that the orbit information in the final combination stream is just copied from one of the incoming streams. The stream used for providing the orbits may vary over time: if the orbit providing stream has an outage then BNC switches to the next remaining stream for getting hold of the orbit information.</p><p>The combination process requires Broadcast Ephemeris. Besides the orbit and clock corrections stream(s) BNC should therefore pull a stream carrying Broadcast Ephemeris in the form of RTCM Version 3 messages.</p><p>It is possible to specify only one Broadcast Ephemeris corrections stream in the combination table. Instead of combining corrections BNC will then merge them with Broadcast Ephemeris to save results in SP3 and/or Clock RINEX format."));
1199 _cmbMaxresLineEdit->setWhatsThis(tr("<p>BNC combines all incoming clocks according to specified weights. Individual clock estimates that differ by more than 'Maximal Residuum' meters from the average of all clocks will be ignored.<p></p>It is suggested to specify a value of about 0.2 m for the Kalman filter combination approach and a value of about 3.0 meters for the Single-Epoch combination approach.</p><p>Default is a value of '999.0'.</p>"));
1200 _cmbSamplSpinBox->setWhatsThis(tr("<p>Specify a combination sampling interval. Clock and orbit corrections will be produced following that interval. A value of 10 sec may be an appropriate choice.</p>"));
1201 _cmbMethodComboBox->setWhatsThis(tr("<p>Select a clock combination approach. Options are 'Single-Epoch' and Kalman 'Filter'. It is suggested to use the Kalman filter approach for the purpose of Precise Point Positioning.</p>"));
1202 _uploadTable->setWhatsThis(tr("<p>BNC can upload clock and orbit corrections to broadcast ephemeris (Broadcast Corrections) in RTCM Version 3 SSR format. You may have a situation where clocks and orbits come from an external Real-time Network Engine (1) or a situation where clock and orbit corrections are combined within BNC (2).</p><p>(1) BNC identifies a stream as coming from a Real-time Network Engine if its format is specified as 'RTNET' and hence its decoder string in the 'Streams' canvas is 'RTNET'. It encodes and uploads that stream to the specified NTRIP broadcaster</p><p>(2) BNC understands that it is expected to encode and upload combined Broadcast Ephemeris corrections if you specify correction streams in the 'Combine Corrections' stream table.</p><p>Hit the 'Add Row' button, double click on the 'Host' field to enter the IP or URL of an NTRIP broadcaster and hit Enter. Then double click on the 'Port', 'Mount' and 'Password' fields to enter the NTRIP broadcaster IP port (default is 80), the mountpoint and the stream upload password. An empty 'Host' option field means that you don't want to upload corrections.</p><p>Select a target coordinate reference system (e.g. IGS08) for outgoing clock and orbit corrections.</p><p>By default orbit and clock corrections refer to Antenna Phase Center (APC). Tick 'CoM' to refer uploaded corrections to Center of Mass instead of APC.</p><p>Specify a path for saving the generated Broadcast Corrections plus Broadcast Ephemeris as SP3 orbit files. If the specified directory does not exist, BNC will not create SP3 orbit files. The following is a path example for a Linux system:<br>/home/user/BNC${GPSWD}.sp3<br>Note that '${GPSWD}' produces the GPS Week and Day number in the file name.</p><ul><li>As an SP3 file contents should be referred to the satellites Center of Mass (CoM) while correctors are referred to the satellites Antenna Phase Center (APC), an offset has to be applied which is available from an IGS ANTEX file. You should therefore specify the 'ANTEX File' path under tab 'PPP (2)' if you want to save the stream contents in SP3 format. If you don't specify an 'ANTEX File' path there, the SP3 file contents will be referred to the satellites APCs.</li></ul></p><p>Specify a path for saving the generated Broadcast Correction clocks plus Broadcast Ephemeris clocks as Clock RINEX files. If the specified directory does not exist, BNC will not create Clock RINEX files. The following is a path example for a Linux system:<br>/home/user/BNC${GPSWD}.clk<br>Note that '${GPSWD}' produces the GPS Week and Day number in the file name.</p><p>Specify finally an SSR Provider ID number, an SSR Solution ID number and an Issue of Data SSR number.</p><p>In case the 'Combine Corrections' table contains only one Broadcast Corrections stream, BNC will merge that stream with Broadcast Ephemeris to save results in files specified here through SP3 and/or Clock RINEX file path. In such a case you should define only the SP3 and Clock RINEX file path and no further options in the 'Upload Corrections' table.</p>"));
1203 addCmbRowButton->setWhatsThis(tr("Hit 'Add Row' button to add another line to the mountpoints table."));
1204 delCmbRowButton->setWhatsThis(tr("Hit 'Delete' button to delete the highlighted line from the mountpoints table."));
1205 addUploadRowButton->setWhatsThis(tr("Hit 'Add Row' button to add another line to the stream upload table."));
1206 delUploadRowButton->setWhatsThis(tr("Hit 'Del Row' button to delete the highlighted line from the stream upload table."));
1207 _uploadIntrComboBox->setWhatsThis(tr("Select the length of the SP3 and Clock RINEX files."));
1208 _uploadSamplRtcmEphCorrSpinBox->setWhatsThis(tr("Select the stream's orbit correction sampling interval in seconds. A value of zero '0' tells BNC to upload all available orbit and clock correction samples together in combined messages."));
1209 _uploadSamplClkRnxSpinBox->setWhatsThis(tr("Select the Clock RINEX file sampling interval in seconds. A value of zero '0' tells BNC to store all available samples into Clock RINEX files."));
1210 _uploadSamplSp3SpinBox->setWhatsThis(tr("Select the SP3 orbit file sampling interval in minutes. A value of zero '0' tells BNC to store all available samples into SP3 orbit files."));
1211 setUploadTrafoButton->setWhatsThis(tr("Hit 'Custom Trafo' to specify your own 14 parameter Helmert Transformation instead of selecting a predefined transformation through 'System' button."));
1212 _uploadEphHostLineEdit->setWhatsThis(tr("BNC can upload a Broadcast Ephemeris stream in RTCM Version 3 format. Specify the host IP of an NTRIP Broadcaster to upload the stream. An empty option field means that you don't want to upload Broadcast Ephemeris."));
1213 _uploadEphPortLineEdit->setWhatsThis(tr("Specify the IP port of an NTRIP Broadcaster to upload the stream. Default is port 80."));
1214 _uploadEphMountpointLineEdit->setWhatsThis(tr("Specify the mounpoint for stream upload to an NTRIP Broadcaster."));
1215 _uploadEphPasswordLineEdit->setWhatsThis(tr("Specify the stream upload password protecting the mounpoint on an NTRIP Broadcaster."));
1216 _uploadEphSampleSpinBox->setWhatsThis(tr("Select the Broadcast Ephemeris sampling interval in seconds. Defaut is '5' meaning that a complete set of Broadcast Ephemeris is uploaded every 5 seconds."));
1217 _uploadEphBytesCounter->setWhatsThis(tr("BNC shows the amount of data uploaded through this stream."));
1218 _actDeleteMountPoints->setWhatsThis(tr("<p>Delete stream(s) from selection presented in the 'Streams' canvas.</p>"));
1219 _actAddMountPoints->setWhatsThis(tr("<p>Add stream(s) to selection presented in the 'Streams' canvas.</p>"));
1220 _actMapMountPoints->setWhatsThis(tr("<p> Draw distribution map of stream selection presented in the 'Streams' canvas. Use the mouse to zoom in or out.</p><p>Left button: Draw rectangle to zoom in.<br>Right button: Zoom out.<br>Middle button: Zoom back.</p>"));
1221 _actStart->setWhatsThis(tr("<p> Start running BNC.</p>"));
1222 _actStop->setWhatsThis(tr("<p> Stop running BNC.</p>"));
1223// Weber
1224
1225 // Enable/Disable all Widgets
1226 // --------------------------
1227 slotBncTextChanged();
1228 enableStartStop();
1229
1230 // Auto start
1231 // ----------
1232 if ( Qt::CheckState(settings.value("autoStart").toInt()) == Qt::Checked) {
1233 slotStart();
1234 }
1235}
1236
1237// Destructor
1238////////////////////////////////////////////////////////////////////////////
1239bncWindow::~bncWindow() {
1240 delete _caster; BNC_CORE->setCaster(0);
1241 delete _casterEph;
1242}
1243
1244//
1245////////////////////////////////////////////////////////////////////////////
1246void bncWindow::populateMountPointsTable() {
1247
1248 for (int iRow = _mountPointsTable->rowCount()-1; iRow >=0; iRow--) {
1249 _mountPointsTable->removeRow(iRow);
1250 }
1251
1252 bncSettings settings;
1253
1254 QListIterator<QString> it(settings.value("mountPoints").toStringList());
1255 int iRow = 0;
1256 while (it.hasNext()) {
1257 QStringList hlp = it.next().split(" ");
1258 if (hlp.size() < 5) continue;
1259 _mountPointsTable->insertRow(iRow);
1260
1261 QUrl url(hlp[0]);
1262
1263 QString fullPath = url.host() + QString(":%1").arg(url.port()) + url.path();
1264 QString format(hlp[1]); QString latitude(hlp[2]); QString longitude(hlp[3]);
1265 QString nmea(hlp[4]);
1266 if (hlp[5] == "S") {
1267 fullPath = hlp[0].replace(0,2,"");
1268 }
1269 QString ntripVersion = "2";
1270 if (hlp.size() >= 6) {
1271 ntripVersion = (hlp[5]);
1272 }
1273
1274 QTableWidgetItem* it;
1275 it = new QTableWidgetItem(url.userInfo());
1276 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1277 _mountPointsTable->setItem(iRow, 0, it);
1278
1279 it = new QTableWidgetItem(fullPath);
1280 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1281 _mountPointsTable->setItem(iRow, 1, it);
1282
1283 it = new QTableWidgetItem(format);
1284 _mountPointsTable->setItem(iRow, 2, it);
1285
1286 if (nmea == "yes") {
1287 it = new QTableWidgetItem(latitude);
1288 _mountPointsTable->setItem(iRow, 3, it);
1289 it = new QTableWidgetItem(longitude);
1290 _mountPointsTable->setItem(iRow, 4, it);
1291 } else {
1292 it = new QTableWidgetItem(latitude);
1293 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1294 _mountPointsTable->setItem(iRow, 3, it);
1295 it = new QTableWidgetItem(longitude);
1296 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1297 _mountPointsTable->setItem(iRow, 4, it);
1298 }
1299
1300 it = new QTableWidgetItem(nmea);
1301 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1302 _mountPointsTable->setItem(iRow, 5, it);
1303
1304 it = new QTableWidgetItem(ntripVersion);
1305 //// it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1306 _mountPointsTable->setItem(iRow, 6, it);
1307
1308 bncTableItem* bncIt = new bncTableItem();
1309 bncIt->setFlags(bncIt->flags() & ~Qt::ItemIsEditable);
1310 _mountPointsTable->setItem(iRow, 7, bncIt);
1311
1312 iRow++;
1313 }
1314
1315 _mountPointsTable->sortItems(1);
1316
1317 enableStartStop();
1318}
1319
1320// Retrieve Table
1321////////////////////////////////////////////////////////////////////////////
1322void bncWindow::slotAddMountPoints() {
1323
1324 bncSettings settings;
1325 QString proxyHost = settings.value("proxyHost").toString();
1326 int proxyPort = settings.value("proxyPort").toInt();
1327 if (proxyHost != _proxyHostLineEdit->text() ||
1328 proxyPort != _proxyPortLineEdit->text().toInt()) {
1329 int iRet = QMessageBox::question(this, "Question", "Proxy options "
1330 "changed. Use the new ones?",
1331 QMessageBox::Yes, QMessageBox::No,
1332 QMessageBox::NoButton);
1333 if (iRet == QMessageBox::Yes) {
1334 settings.setValue("proxyHost", _proxyHostLineEdit->text());
1335 settings.setValue("proxyPort", _proxyPortLineEdit->text());
1336 }
1337 }
1338
1339 settings.setValue("sslCaCertPath", _sslCaCertPathLineEdit->text());
1340 settings.setValue("ignoreSslErrors", _ignoreSslErrorsCheckBox->checkState());
1341
1342 QMessageBox msgBox;
1343 msgBox.setIcon(QMessageBox::Question);
1344 msgBox.setWindowTitle("Add Stream");
1345 msgBox.setText("Add stream(s) coming from:");
1346
1347 QPushButton* buttonNtrip = msgBox.addButton(tr("Caster"), QMessageBox::ActionRole);
1348 QPushButton* buttonIP = msgBox.addButton(tr("TCP/IP port"), QMessageBox::ActionRole);
1349 QPushButton* buttonUDP = msgBox.addButton(tr("UDP port"), QMessageBox::ActionRole);
1350 QPushButton* buttonSerial = msgBox.addButton(tr("Serial port"), QMessageBox::ActionRole);
1351 QPushButton* buttonCancel = msgBox.addButton(tr("Cancel"), QMessageBox::ActionRole);
1352
1353 msgBox.exec();
1354
1355 if (msgBox.clickedButton() == buttonNtrip) {
1356 bncTableDlg* dlg = new bncTableDlg(this);
1357 dlg->move(this->pos().x()+50, this->pos().y()+50);
1358 connect(dlg, SIGNAL(newMountPoints(QStringList*)),
1359 this, SLOT(slotNewMountPoints(QStringList*)));
1360 dlg->exec();
1361 delete dlg;
1362 } else if (msgBox.clickedButton() == buttonIP) {
1363 bncIpPort* ipp = new bncIpPort(this);
1364 connect(ipp, SIGNAL(newMountPoints(QStringList*)),
1365 this, SLOT(slotNewMountPoints(QStringList*)));
1366 ipp->exec();
1367 delete ipp;
1368 } else if (msgBox.clickedButton() == buttonUDP) {
1369 bncUdpPort* udp = new bncUdpPort(this);
1370 connect(udp, SIGNAL(newMountPoints(QStringList*)),
1371 this, SLOT(slotNewMountPoints(QStringList*)));
1372 udp->exec();
1373 delete udp;
1374 } else if (msgBox.clickedButton() == buttonSerial) {
1375 bncSerialPort* sep = new bncSerialPort(this);
1376 connect(sep, SIGNAL(newMountPoints(QStringList*)),
1377 this, SLOT(slotNewMountPoints(QStringList*)));
1378 sep->exec();
1379 delete sep;
1380 } else if (msgBox.clickedButton() == buttonCancel) {
1381 // Cancel
1382 }
1383
1384 enableStartStop();
1385}
1386
1387// Delete Selected Mount Points
1388////////////////////////////////////////////////////////////////////////////
1389void bncWindow::slotDeleteMountPoints() {
1390
1391 int nRows = _mountPointsTable->rowCount();
1392 bool flg[nRows];
1393 for (int iRow = 0; iRow < nRows; iRow++) {
1394 if (_mountPointsTable->isItemSelected(_mountPointsTable->item(iRow,1))) {
1395 flg[iRow] = true;
1396 }
1397 else {
1398 flg[iRow] = false;
1399 }
1400 }
1401 for (int iRow = nRows-1; iRow >= 0; iRow--) {
1402 if (flg[iRow]) {
1403 _mountPointsTable->removeRow(iRow);
1404 }
1405 }
1406 _actDeleteMountPoints->setEnabled(false);
1407
1408 enableStartStop();
1409}
1410
1411// New Mount Points Selected
1412////////////////////////////////////////////////////////////////////////////
1413void bncWindow::slotNewMountPoints(QStringList* mountPoints) {
1414 int iRow = 0;
1415 QListIterator<QString> it(*mountPoints);
1416 while (it.hasNext()) {
1417 QStringList hlp = it.next().split(" ");
1418 QUrl url(hlp[0]);
1419 QString fullPath = url.host() + QString(":%1").arg(url.port()) + url.path();
1420 QString format(hlp[1]); QString latitude(hlp[2]); QString longitude(hlp[3]);
1421 QString nmea(hlp[4]);
1422 if (hlp[5] == "S") {
1423 fullPath = hlp[0].replace(0,2,"");
1424 }
1425 QString ntripVersion = "2";
1426 if (hlp.size() >= 6) {
1427 ntripVersion = (hlp[5]);
1428 }
1429
1430 _mountPointsTable->insertRow(iRow);
1431
1432 QTableWidgetItem* it;
1433 it = new QTableWidgetItem(url.userInfo());
1434 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1435 _mountPointsTable->setItem(iRow, 0, it);
1436
1437 it = new QTableWidgetItem(fullPath);
1438 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1439 _mountPointsTable->setItem(iRow, 1, it);
1440
1441 it = new QTableWidgetItem(format);
1442 _mountPointsTable->setItem(iRow, 2, it);
1443
1444 if (nmea == "yes") {
1445 it = new QTableWidgetItem(latitude);
1446 _mountPointsTable->setItem(iRow, 3, it);
1447 it = new QTableWidgetItem(longitude);
1448 _mountPointsTable->setItem(iRow, 4, it);
1449 } else {
1450 it = new QTableWidgetItem(latitude);
1451 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1452 _mountPointsTable->setItem(iRow, 3, it);
1453 it = new QTableWidgetItem(longitude);
1454 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1455 _mountPointsTable->setItem(iRow, 4, it);
1456 }
1457
1458 it = new QTableWidgetItem(nmea);
1459 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1460 _mountPointsTable->setItem(iRow, 5, it);
1461
1462 it = new QTableWidgetItem(ntripVersion);
1463 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
1464 _mountPointsTable->setItem(iRow, 6, it);
1465
1466 bncTableItem* bncIt = new bncTableItem();
1467 _mountPointsTable->setItem(iRow, 7, bncIt);
1468
1469 iRow++;
1470 }
1471 _mountPointsTable->hideColumn(0);
1472 _mountPointsTable->sortItems(1);
1473 delete mountPoints;
1474
1475 enableStartStop();
1476}
1477
1478// Save Options (serialize)
1479////////////////////////////////////////////////////////////////////////////
1480void bncWindow::slotSaveOptions() {
1481 saveOptions();
1482 bncSettings settings;
1483 settings.sync();
1484}
1485
1486// Save Options (memory only)
1487////////////////////////////////////////////////////////////////////////////
1488void bncWindow::saveOptions() {
1489
1490 QStringList mountPoints;
1491 for (int iRow = 0; iRow < _mountPointsTable->rowCount(); iRow++) {
1492
1493 if (_mountPointsTable->item(iRow, 6)->text() != "S") {
1494 QUrl url( "//" + _mountPointsTable->item(iRow, 0)->text() +
1495 "@" + _mountPointsTable->item(iRow, 1)->text() );
1496
1497 mountPoints.append(url.toString() + " " +
1498 _mountPointsTable->item(iRow, 2)->text()
1499 + " " + _mountPointsTable->item(iRow, 3)->text()
1500 + " " + _mountPointsTable->item(iRow, 4)->text()
1501 + " " + _mountPointsTable->item(iRow, 5)->text()
1502 + " " + _mountPointsTable->item(iRow, 6)->text());
1503 } else {
1504 mountPoints.append(
1505 "//" + _mountPointsTable->item(iRow, 1)->text()
1506 + " " + _mountPointsTable->item(iRow, 2)->text()
1507 + " " + _mountPointsTable->item(iRow, 3)->text()
1508 + " " + _mountPointsTable->item(iRow, 4)->text()
1509 + " " + _mountPointsTable->item(iRow, 5)->text()
1510 + " " + _mountPointsTable->item(iRow, 6)->text());
1511 }
1512 }
1513
1514 QStringList combineStreams;
1515 for (int iRow = 0; iRow < _cmbTable->rowCount(); iRow++) {
1516 QString hlp;
1517 for (int iCol = 0; iCol < _cmbTable->columnCount(); iCol++) {
1518 if (_cmbTable->item(iRow, iCol)) {
1519 hlp += _cmbTable->item(iRow, iCol)->text() + " ";
1520 }
1521 }
1522 if (!hlp.isEmpty()) {
1523 combineStreams << hlp;
1524 }
1525 }
1526
1527 QStringList uploadMountpointsOut;
1528 for (int iRow = 0; iRow < _uploadTable->rowCount(); iRow++) {
1529 QString hlp;
1530 for (int iCol = 0; iCol < _uploadTable->columnCount(); iCol++) {
1531 if (_uploadTable->cellWidget(iRow, iCol) &&
1532 (iCol == 3 || iCol == 4 || iCol == 5)) {
1533 if (iCol == 3) {
1534 QLineEdit* passwd = (QLineEdit*)(_uploadTable->cellWidget(iRow, iCol));
1535 hlp += passwd->text() + ",";
1536 }
1537 else if (iCol == 4) {
1538 QComboBox* system = (QComboBox*)(_uploadTable->cellWidget(iRow, iCol));
1539 hlp += system->currentText() + ",";
1540 }
1541 else if (iCol == 5) {
1542 QCheckBox* com = (QCheckBox*)(_uploadTable->cellWidget(iRow, iCol));
1543 QString state; state.setNum(com->checkState());
1544 hlp += state + ",";
1545 }
1546 }
1547 else if (_uploadTable->item(iRow, iCol)) {
1548 hlp += _uploadTable->item(iRow, iCol)->text() + ",";
1549 }
1550 }
1551 if (!hlp.isEmpty()) {
1552 uploadMountpointsOut << hlp;
1553 }
1554 }
1555
1556 bncSettings settings;
1557
1558 settings.setValue("startTab", _aogroup->currentIndex());
1559 settings.setValue("statusTab", _loggroup->currentIndex());
1560 settings.setValue("mountPoints", mountPoints);
1561// Network
1562 settings.setValue("proxyHost", _proxyHostLineEdit->text());
1563 settings.setValue("proxyPort", _proxyPortLineEdit->text());
1564 settings.setValue("sslCaCertPath", _sslCaCertPathLineEdit->text());
1565 settings.setValue("ignoreSslErrors", _ignoreSslErrorsCheckBox->checkState());
1566// General
1567 settings.setValue("logFile", _logFileLineEdit->text());
1568 settings.setValue("rnxAppend", _rnxAppendCheckBox->checkState());
1569 settings.setValue("onTheFlyInterval", _onTheFlyComboBox->currentText());
1570 settings.setValue("autoStart", _autoStartCheckBox->checkState());
1571 settings.setValue("rawOutFile", _rawOutFileLineEdit->text());
1572// RINEX Observations
1573 settings.setValue("rnxPath", _rnxPathLineEdit->text());
1574 settings.setValue("rnxIntr", _rnxIntrComboBox->currentText());
1575 settings.setValue("rnxSampl", _rnxSamplSpinBox->value());
1576 settings.setValue("rnxSkel", _rnxSkelLineEdit->text());
1577 settings.setValue("rnxScript", _rnxScrpLineEdit->text());
1578 settings.setValue("rnxV3", _rnxV3CheckBox->checkState());
1579// RINEX Ephemeris
1580 settings.setValue("ephPath", _ephPathLineEdit->text());
1581 settings.setValue("ephIntr", _ephIntrComboBox->currentText());
1582 settings.setValue("outEphPort", _outEphPortLineEdit->text());
1583 settings.setValue("ephV3", _ephV3CheckBox->checkState());
1584// Broadcast Corrections
1585 settings.setValue("corrPath", _corrPathLineEdit->text());
1586 settings.setValue("corrIntr", _corrIntrComboBox->currentText());
1587 settings.setValue("corrPort", _corrPortLineEdit->text());
1588 settings.setValue("corrTime", _corrTimeSpinBox->value());
1589// Feed Engine
1590 settings.setValue("outPort", _outPortLineEdit->text());
1591 settings.setValue("waitTime", _waitTimeSpinBox->value());
1592 settings.setValue("binSampl", _binSamplSpinBox->value());
1593 settings.setValue("outFile", _outFileLineEdit->text());
1594 settings.setValue("outUPort", _outUPortLineEdit->text());
1595// Serial Output
1596 settings.setValue("serialMountPoint",_serialMountPointLineEdit->text());
1597 settings.setValue("serialPortName", _serialPortNameLineEdit->text());
1598 settings.setValue("serialBaudRate", _serialBaudRateComboBox->currentText());
1599 settings.setValue("serialFlowControl",_serialFlowControlComboBox->currentText());
1600 settings.setValue("serialDataBits", _serialDataBitsComboBox->currentText());
1601 settings.setValue("serialParity", _serialParityComboBox->currentText());
1602 settings.setValue("serialStopBits", _serialStopBitsComboBox->currentText());
1603 settings.setValue("serialAutoNMEA", _serialAutoNMEAComboBox->currentText());
1604 settings.setValue("serialFileNMEA",_serialFileNMEALineEdit->text());
1605 settings.setValue("serialHeightNMEA",_serialHeightNMEALineEdit->text());
1606// Outages
1607 settings.setValue("obsRate", _obsRateComboBox->currentText());
1608 settings.setValue("adviseFail", _adviseFailSpinBox->value());
1609 settings.setValue("adviseReco", _adviseRecoSpinBox->value());
1610 settings.setValue("adviseScript",_adviseScriptLineEdit->text());
1611// Miscellaneous
1612 settings.setValue("miscMount", _miscMountLineEdit->text());
1613 settings.setValue("miscPort", _miscPortLineEdit->text());
1614 settings.setValue("perfIntr", _perfIntrComboBox->currentText());
1615 settings.setValue("scanRTCM", _scanRTCMCheckBox->checkState());
1616// Reqc
1617 settings.setValue("reqcAction", _reqcActionComboBox->currentText());
1618 settings.setValue("reqcObsFile", _reqcObsFileChooser->fileName());
1619 settings.setValue("reqcNavFile", _reqcNavFileChooser->fileName());
1620 settings.setValue("reqcOutObsFile", _reqcOutObsLineEdit->text());
1621 settings.setValue("reqcOutNavFile", _reqcOutNavLineEdit->text());
1622 settings.setValue("reqcOutLogFile", _reqcOutLogLineEdit->text());
1623 settings.setValue("reqcPlotDir", _reqcPlotDirLineEdit->text());
1624 settings.setValue("reqcSkyPlotSystems", _reqcSkyPlotSystems->currentText());
1625// Combine Corrections
1626 if (!combineStreams.isEmpty()) {
1627 settings.setValue("combineStreams", combineStreams);
1628 }
1629 else {
1630 settings.setValue("combineStreams", "");
1631 }
1632 settings.setValue("cmbMethod", _cmbMethodComboBox->currentText());
1633 settings.setValue("cmbMaxres", _cmbMaxresLineEdit->text());
1634 settings.setValue("cmbSampl", _cmbSamplSpinBox->value());
1635// Upload Corrections
1636 if (!uploadMountpointsOut.isEmpty()) {
1637 settings.setValue("uploadMountpointsOut", uploadMountpointsOut);
1638 }
1639 else {
1640 settings.setValue("uploadMountpointsOut", "");
1641 }
1642 settings.setValue("uploadIntr", _uploadIntrComboBox->currentText());
1643 settings.setValue("uploadSamplRtcmEphCorr", _uploadSamplRtcmEphCorrSpinBox->value());
1644 settings.setValue("uploadSamplSp3", _uploadSamplSp3SpinBox->value());
1645 settings.setValue("uploadSamplClkRnx", _uploadSamplClkRnxSpinBox->value());
1646// Upload Ephemeris
1647 settings.setValue("uploadEphHost", _uploadEphHostLineEdit->text());
1648 settings.setValue("uploadEphPort", _uploadEphPortLineEdit->text());
1649 settings.setValue("uploadEphMountpoint",_uploadEphMountpointLineEdit->text());
1650 settings.setValue("uploadEphPassword", _uploadEphPasswordLineEdit->text());
1651 settings.setValue("uploadEphSample", _uploadEphSampleSpinBox->value());
1652
1653 if (_caster) {
1654 _caster->readMountPoints();
1655 }
1656
1657 _pppWidgets.saveOptions();
1658}
1659
1660// All get slots terminated
1661////////////////////////////////////////////////////////////////////////////
1662void bncWindow::slotGetThreadsFinished() {
1663 BNC_CORE->slotMessage("All Get Threads Terminated", true);
1664 delete _caster; _caster = 0; BNC_CORE->setCaster(0);
1665 delete _casterEph; _casterEph = 0;
1666 _runningRealTime = false;
1667}
1668
1669// Start It!
1670////////////////////////////////////////////////////////////////////////////
1671void bncWindow::slotStart() {
1672 saveOptions();
1673 if ( _pppWidgets._dataSource->currentText() == "RINEX Files") {
1674 BNC_CORE->startPPP();
1675 }
1676 else if ( !_reqcActionComboBox->currentText().isEmpty() ) {
1677 startPostProcessingReqc();
1678 }
1679 else {
1680 startRealTime();
1681 BNC_CORE->startPPP();
1682 }
1683}
1684
1685// Start Real-Time (Retrieve Data etc.)
1686////////////////////////////////////////////////////////////////////////////
1687void bncWindow::startRealTime() {
1688
1689 _runningRealTime = true;
1690
1691 _bncFigurePPP->reset();
1692
1693 _actDeleteMountPoints->setEnabled(false);
1694
1695 enableStartStop();
1696
1697 _caster = new bncCaster();
1698
1699 BNC_CORE->setCaster(_caster);
1700 BNC_CORE->setPort(_outEphPortLineEdit->text().toInt());
1701 BNC_CORE->setPortCorr(_corrPortLineEdit->text().toInt());
1702 BNC_CORE->initCombination();
1703
1704 connect(_caster, SIGNAL(getThreadsFinished()),
1705 this, SLOT(slotGetThreadsFinished()));
1706
1707 connect (_caster, SIGNAL(mountPointsRead(QList<bncGetThread*>)),
1708 this, SLOT(slotMountPointsRead(QList<bncGetThread*>)));
1709
1710 BNC_CORE->slotMessage("========== Start BNC v" BNCVERSION " =========", true);
1711
1712 bncSettings settings;
1713
1714 QDir rnxdir(settings.value("rnxPath").toString());
1715 if (!rnxdir.exists()) BNC_CORE->slotMessage("Cannot find RINEX Observations directory", true);
1716
1717 QString rnx_file = settings.value("rnxScript").toString();
1718 if ( !rnx_file.isEmpty() ) {
1719 QFile rnxfile(settings.value("rnxScript").toString());
1720 if (!rnxfile.exists()) BNC_CORE->slotMessage("Cannot find RINEX Observations script", true);
1721 }
1722
1723 QDir ephdir(settings.value("ephPath").toString());
1724 if (!ephdir.exists()) BNC_CORE->slotMessage("Cannot find RINEX Ephemeris directory", true);
1725
1726 QDir corrdir(settings.value("corrPath").toString());
1727 if (!corrdir.exists()) BNC_CORE->slotMessage("Cannot find Broadcast Corrections directory", true);
1728
1729 QString advise_file = settings.value("adviseScript").toString();
1730 if ( !advise_file.isEmpty() ) {
1731 QFile advisefile(settings.value("adviseScript").toString());
1732 if (!advisefile.exists()) BNC_CORE->slotMessage("Cannot find Outages script", true);
1733 }
1734
1735 QString ant_file = settings.value("pppAntex").toString();
1736 if ( !ant_file.isEmpty() ) {
1737 QFile anxfile(settings.value("pppAntex").toString());
1738 if (!anxfile.exists()) BNC_CORE->slotMessage("Cannot find IGS ANTEX file", true);
1739 }
1740
1741 _caster->readMountPoints();
1742
1743 _casterEph = new bncEphUploadCaster();
1744 connect(_casterEph, SIGNAL(newBytes(QByteArray,double)),
1745 _uploadEphBytesCounter, SLOT(slotNewBytes(QByteArray,double)));
1746}
1747
1748// Retrieve Data
1749////////////////////////////////////////////////////////////////////////////
1750void bncWindow::slotStop() {
1751 int iRet = QMessageBox::question(this, "Stop", "Stop retrieving data?",
1752 QMessageBox::Yes, QMessageBox::No,
1753 QMessageBox::NoButton);
1754 if (iRet == QMessageBox::Yes) {
1755 BNC_CORE->stopPPP();
1756 BNC_CORE->stopCombination();
1757 delete _caster; _caster = 0; BNC_CORE->setCaster(0);
1758 delete _casterEph; _casterEph = 0;
1759 _runningRealTime = false;
1760 enableStartStop();
1761 }
1762}
1763
1764// Close Application gracefully
1765////////////////////////////////////////////////////////////////////////////
1766void bncWindow::closeEvent(QCloseEvent* event) {
1767
1768 int iRet = QMessageBox::question(this, "Close", "Save Options?",
1769 QMessageBox::Yes, QMessageBox::No,
1770 QMessageBox::Cancel);
1771
1772 if (iRet == QMessageBox::Cancel) {
1773 event->ignore();
1774 return;
1775 }
1776 else if (iRet == QMessageBox::Yes) {
1777 slotSaveOptions();
1778 }
1779
1780 BNC_CORE->stopPPP();
1781
1782 QMainWindow::closeEvent(event);
1783}
1784
1785// User changed the selection of mountPoints
1786////////////////////////////////////////////////////////////////////////////
1787void bncWindow::slotSelectionChanged() {
1788 if (_mountPointsTable->selectedItems().isEmpty()) {
1789 _actDeleteMountPoints->setEnabled(false);
1790 }
1791 else {
1792 _actDeleteMountPoints->setEnabled(true);
1793 }
1794}
1795
1796// Display Program Messages
1797////////////////////////////////////////////////////////////////////////////
1798void bncWindow::slotWindowMessage(const QByteArray msg, bool showOnScreen) {
1799 if (showOnScreen ) {
1800 _log->append(QDateTime::currentDateTime().toUTC().toString("yy-MM-dd hh:mm:ss ") + msg + '\n');
1801 }
1802}
1803
1804// About Message
1805////////////////////////////////////////////////////////////////////////////
1806void bncWindow::slotAbout() {
1807 new bncAboutDlg(0);
1808}
1809
1810//Flowchart
1811////////////////////////////////////////////////////////////////////////////
1812void bncWindow::slotFlowchart() {
1813 new bncFlowchartDlg(0);
1814}
1815
1816// Help Window
1817////////////////////////////////////////////////////////////////////////////
1818void bncWindow::slotHelp() {
1819 QUrl url;
1820 url.setPath(":bnchelp.html");
1821 new bncHlpDlg(0, url);
1822}
1823
1824// Select Fonts
1825////////////////////////////////////////////////////////////////////////////
1826void bncWindow::slotFontSel() {
1827 bool ok;
1828 QFont newFont = QFontDialog::getFont(&ok, this->font(), this);
1829 if (ok) {
1830 bncSettings settings;
1831 settings.setValue("font", newFont.toString());
1832 QApplication::setFont(newFont);
1833 int ww = QFontMetrics(newFont).width('w');
1834 setMinimumSize(60*ww, 80*ww);
1835 resize(60*ww, 80*ww);
1836 }
1837}
1838
1839// Whats This Help
1840void bncWindow::slotWhatsThis() {
1841 QWhatsThis::enterWhatsThisMode();
1842}
1843
1844//
1845////////////////////////////////////////////////////////////////////////////
1846void bncWindow::slotMountPointsRead(QList<bncGetThread*> threads) {
1847 _threads = threads;
1848
1849 _bncFigure->updateMountPoints();
1850 _bncFigureLate->updateMountPoints();
1851
1852 populateMountPointsTable();
1853 bncSettings settings;
1854 _binSamplSpinBox->setValue(settings.value("binSampl").toInt());
1855 _waitTimeSpinBox->setValue(settings.value("waitTime").toInt());
1856 QListIterator<bncGetThread*> iTh(threads);
1857 while (iTh.hasNext()) {
1858 bncGetThread* thread = iTh.next();
1859 for (int iRow = 0; iRow < _mountPointsTable->rowCount(); iRow++) {
1860 QUrl url( "//" + _mountPointsTable->item(iRow, 0)->text() +
1861 "@" + _mountPointsTable->item(iRow, 1)->text() );
1862 if (url == thread->mountPoint() &&
1863 _mountPointsTable->item(iRow, 3)->text() == thread->latitude() &&
1864 _mountPointsTable->item(iRow, 4)->text() == thread->longitude() ) {
1865 ((bncTableItem*) _mountPointsTable->item(iRow, 7))->setGetThread(thread);
1866 disconnect(thread, SIGNAL(newBytes(QByteArray, double)),
1867 _bncFigure, SLOT(slotNewData(QByteArray, double)));
1868 connect(thread, SIGNAL(newBytes(QByteArray, double)),
1869 _bncFigure, SLOT(slotNewData(QByteArray, double)));
1870 disconnect(thread, SIGNAL(newLatency(QByteArray, double)),
1871 _bncFigureLate, SLOT(slotNewLatency(QByteArray, double)));
1872 connect(thread, SIGNAL(newLatency(QByteArray, double)),
1873 _bncFigureLate, SLOT(slotNewLatency(QByteArray, double)));
1874 break;
1875 }
1876 }
1877 }
1878}
1879
1880//
1881////////////////////////////////////////////////////////////////////////////
1882void bncWindow::CreateMenu() {
1883 // Create Menus
1884 // ------------
1885 _menuFile = menuBar()->addMenu(tr("&File"));
1886 _menuFile->addAction(_actFontSel);
1887 _menuFile->addSeparator();
1888 _menuFile->addAction(_actSaveOpt);
1889 _menuFile->addSeparator();
1890 _menuFile->addAction(_actQuit);
1891
1892 _menuHlp = menuBar()->addMenu(tr("&Help"));
1893 _menuHlp->addAction(_actHelp);
1894 _menuHlp->addAction(_actFlowchart);
1895 _menuHlp->addAction(_actAbout);
1896}
1897
1898// Toolbar
1899////////////////////////////////////////////////////////////////////////////
1900void bncWindow::AddToolbar() {
1901 QToolBar* toolBar = new QToolBar;
1902 addToolBar(Qt::BottomToolBarArea, toolBar);
1903 toolBar->setMovable(false);
1904 toolBar->addAction(_actAddMountPoints);
1905 toolBar->addAction(_actDeleteMountPoints);
1906 toolBar->addAction(_actMapMountPoints);
1907 toolBar->addAction(_actStart);
1908 toolBar->addAction(_actStop);
1909 toolBar->addWidget(new QLabel(" "));
1910 toolBar->addAction(_actwhatsthis);
1911}
1912
1913// About
1914////////////////////////////////////////////////////////////////////////////
1915bncAboutDlg::bncAboutDlg(QWidget* parent) :
1916 QDialog(parent) {
1917
1918 QTextBrowser* tb = new QTextBrowser;
1919 QUrl url; url.setPath(":bncabout.html");
1920 tb->setSource(url);
1921 tb->setReadOnly(true);
1922
1923 int ww = QFontMetrics(font()).width('w');
1924 QPushButton* _closeButton = new QPushButton("Close");
1925 _closeButton->setMaximumWidth(10*ww);
1926 connect(_closeButton, SIGNAL(clicked()), this, SLOT(close()));
1927
1928 QGridLayout* dlgLayout = new QGridLayout();
1929 QLabel* img = new QLabel();
1930 img->setPixmap(QPixmap(":ntrip-logo.png"));
1931 dlgLayout->addWidget(img, 0,0);
1932 dlgLayout->addWidget(new QLabel("BKG Ntrip Client (BNC) Version "BNCVERSION), 0,1);
1933 dlgLayout->addWidget(tb,1,0,1,2);
1934 dlgLayout->addWidget(_closeButton,2,1,Qt::AlignRight);
1935
1936 setLayout(dlgLayout);
1937 resize(60*ww, 60*ww);
1938 setWindowTitle("About BNC");
1939 show();
1940}
1941
1942//
1943////////////////////////////////////////////////////////////////////////////
1944bncAboutDlg::~bncAboutDlg() {
1945};
1946
1947// Flowchart
1948////////////////////////////////////////////////////////////////////////////
1949bncFlowchartDlg::bncFlowchartDlg(QWidget* parent) :
1950 QDialog(parent) {
1951
1952 int ww = QFontMetrics(font()).width('w');
1953 QPushButton* _closeButton = new QPushButton("Close");
1954 _closeButton->setMaximumWidth(10*ww);
1955 connect(_closeButton, SIGNAL(clicked()), this, SLOT(close()));
1956
1957 QGridLayout* dlgLayout = new QGridLayout();
1958 QLabel* img = new QLabel();
1959 img->setPixmap(QPixmap(":bncflowchart.png"));
1960 dlgLayout->addWidget(img, 0,0);
1961 dlgLayout->addWidget(_closeButton,1,0,Qt::AlignLeft);
1962
1963 setLayout(dlgLayout);
1964 setWindowTitle("Flow Chart");
1965 show();
1966}
1967
1968//
1969////////////////////////////////////////////////////////////////////////////
1970bncFlowchartDlg::~bncFlowchartDlg() {
1971};
1972
1973// Enable/Disable Widget (and change its color)
1974////////////////////////////////////////////////////////////////////////////
1975void bncWindow::enableWidget(bool enable, QWidget* widget) {
1976 const static QPalette paletteWhite(QColor(255, 255, 255));
1977 const static QPalette paletteGray(QColor(230, 230, 230));
1978
1979 widget->setEnabled(enable);
1980 if (enable) {
1981 widget->setPalette(paletteWhite);
1982 }
1983 else {
1984 widget->setPalette(paletteGray);
1985 }
1986}
1987
1988// Bnc Text
1989////////////////////////////////////////////////////////////////////////////
1990void bncWindow::slotBncTextChanged(){
1991
1992 bool enable = true;
1993
1994 // Proxy
1995 //------
1996 if (sender() == 0 || sender() == _proxyHostLineEdit) {
1997 enable = !_proxyHostLineEdit->text().isEmpty();
1998 enableWidget(enable, _proxyPortLineEdit);
1999 }
2000
2001 // RINEX Observations
2002 // ------------------
2003 if (sender() == 0 || sender() == _rnxPathLineEdit) {
2004 enable = !_rnxPathLineEdit->text().isEmpty();
2005 enableWidget(enable, _rnxSamplSpinBox);
2006 enableWidget(enable, _rnxSkelLineEdit);
2007 enableWidget(enable, _rnxScrpLineEdit);
2008 enableWidget(enable, _rnxV3CheckBox);
2009 enableWidget(enable, _rnxIntrComboBox);
2010 }
2011
2012 // RINEX Ephemeris
2013 // ---------------
2014 if (sender() == 0 || sender() == _ephPathLineEdit || sender() == _outEphPortLineEdit) {
2015 enable = !_ephPathLineEdit->text().isEmpty() || !_outEphPortLineEdit->text().isEmpty();
2016 enableWidget(enable, _ephIntrComboBox);
2017 enableWidget(enable, _ephV3CheckBox);
2018 }
2019
2020 // Broadcast Corrections
2021 // ---------------------
2022 if (sender() == 0 || sender() == _corrPathLineEdit || sender() == _corrPortLineEdit) {
2023 enable = !_corrPathLineEdit->text().isEmpty() || !_corrPortLineEdit->text().isEmpty();
2024 enableWidget(enable, _corrIntrComboBox);
2025 }
2026
2027 // Feed Engine
2028 // -----------
2029 if (sender() == 0 || sender() == _outPortLineEdit || sender() == _outFileLineEdit) {
2030 enable = !_outPortLineEdit->text().isEmpty() || !_outFileLineEdit->text().isEmpty();
2031 enableWidget(enable, _waitTimeSpinBox);
2032 enableWidget(enable, _binSamplSpinBox);
2033 }
2034
2035 // Serial Output
2036 // -------------
2037 if (sender() == 0 || sender() == _serialMountPointLineEdit ||
2038 sender() == _serialAutoNMEAComboBox) {
2039 enable = !_serialMountPointLineEdit->text().isEmpty();
2040 enableWidget(enable, _serialPortNameLineEdit);
2041 enableWidget(enable, _serialBaudRateComboBox);
2042 enableWidget(enable, _serialParityComboBox);
2043 enableWidget(enable, _serialDataBitsComboBox);
2044 enableWidget(enable, _serialStopBitsComboBox);
2045 enableWidget(enable, _serialFlowControlComboBox);
2046 enableWidget(enable, _serialAutoNMEAComboBox);
2047
2048 bool enable2 = enable && _serialAutoNMEAComboBox->currentText() != "Auto";
2049 enableWidget(enable2, _serialFileNMEALineEdit);
2050 }
2051
2052 // Outages
2053 // -------
2054 if (sender() == 0 || sender() == _obsRateComboBox) {
2055 enable = !_obsRateComboBox->currentText().isEmpty();
2056 enableWidget(enable, _adviseFailSpinBox);
2057 enableWidget(enable, _adviseRecoSpinBox);
2058 enableWidget(enable, _adviseScriptLineEdit);
2059 }
2060
2061 // Miscellaneous
2062 // -------------
2063 if (sender() == 0 || sender() == _miscMountLineEdit) {
2064 enable = !_miscMountLineEdit->text().isEmpty();
2065 enableWidget(enable, _perfIntrComboBox);
2066 enableWidget(enable, _scanRTCMCheckBox);
2067 enableWidget(enable, _miscPortLineEdit);
2068 }
2069
2070 // Enable/disable Broadcast Ephemerides
2071 // ------------------------------------
2072 if (sender() == 0 || sender() == _uploadEphHostLineEdit) {
2073 if (!_uploadEphHostLineEdit->text().isEmpty()) {
2074 _uploadEphPortLineEdit->setStyleSheet("background-color: white");
2075 _uploadEphMountpointLineEdit->setStyleSheet("background-color: white");
2076 _uploadEphPasswordLineEdit->setStyleSheet("background-color: white");
2077 _uploadEphSampleSpinBox->setStyleSheet("background-color: white");
2078 _uploadEphPortLineEdit->setEnabled(true);
2079 _uploadEphMountpointLineEdit->setEnabled(true);
2080 _uploadEphPasswordLineEdit->setEnabled(true);
2081 _uploadEphSampleSpinBox->setEnabled(true);
2082 }
2083 else {
2084 _uploadEphPortLineEdit->setStyleSheet("background-color: lightGray");
2085 _uploadEphMountpointLineEdit->setStyleSheet("background-color: lightGray");
2086 _uploadEphPasswordLineEdit->setStyleSheet("background-color: lightGray");
2087 _uploadEphSampleSpinBox->setStyleSheet("background-color: lightGray");
2088 _uploadEphPortLineEdit->setEnabled(false);
2089 _uploadEphMountpointLineEdit->setEnabled(false);
2090 _uploadEphPasswordLineEdit->setEnabled(false);
2091 _uploadEphSampleSpinBox->setEnabled(false);
2092 }
2093 }
2094
2095 // Combine Corrections
2096 // -------------------
2097 if (sender() == 0 || sender() == _cmbTable) {
2098 int iRow = _cmbTable->rowCount();
2099 if (iRow > 0) {
2100 enableWidget(true, _cmbMethodComboBox);
2101 _cmbMaxresLineEdit->setStyleSheet("background-color: white");
2102 _cmbMaxresLineEdit->setEnabled(true);
2103 _cmbSamplSpinBox->setEnabled(true);
2104 }
2105 else {
2106 enableWidget(false, _cmbMethodComboBox);
2107 _cmbMaxresLineEdit->setStyleSheet("background-color: lightGray");
2108 _cmbMaxresLineEdit->setEnabled(false);
2109 _cmbSamplSpinBox->setEnabled(false);
2110 }
2111 }
2112
2113 // Upload(clk)
2114 // -----------
2115 int iRow = _uploadTable->rowCount();
2116 if (iRow > 0) {
2117 enableWidget(true, _uploadIntrComboBox);
2118 enableWidget(true, _uploadSamplRtcmEphCorrSpinBox);
2119 enableWidget(true, _uploadSamplClkRnxSpinBox);
2120 enableWidget(true, _uploadSamplSp3SpinBox);
2121 }
2122 else {
2123 enableWidget(false, _uploadIntrComboBox);
2124 enableWidget(false, _uploadSamplRtcmEphCorrSpinBox);
2125 enableWidget(false, _uploadSamplClkRnxSpinBox);
2126 enableWidget(false, _uploadSamplSp3SpinBox);
2127 }
2128
2129 // QC
2130 // --
2131 if (sender() == 0 || sender() == _reqcActionComboBox) {
2132 enable = !_reqcActionComboBox->currentText().isEmpty();
2133 bool enable10 = _reqcActionComboBox->currentText() == "Edit/Concatenate";
2134 enableWidget(enable && enable10, _reqcEditOptionButton);
2135 enableWidget(enable, _reqcObsFileChooser);
2136 enableWidget(enable, _reqcNavFileChooser);
2137 enableWidget(enable && enable10, _reqcOutObsLineEdit);
2138 enableWidget(enable && enable10, _reqcOutNavLineEdit);
2139 enableWidget(enable, _reqcOutLogLineEdit);
2140 enableWidget(enable && !enable10, _reqcPlotDirLineEdit);
2141 enableWidget(enable && !enable10, _reqcSkyPlotSystems);
2142 }
2143
2144 enableStartStop();
2145}
2146
2147//
2148////////////////////////////////////////////////////////////////////////////
2149void bncWindow::slotAddCmbRow() {
2150 int iRow = _cmbTable->rowCount();
2151 _cmbTable->insertRow(iRow);
2152 for (int iCol = 0; iCol < _cmbTable->columnCount(); iCol++) {
2153 _cmbTable->setItem(iRow, iCol, new QTableWidgetItem(""));
2154 }
2155}
2156
2157//
2158////////////////////////////////////////////////////////////////////////////
2159void bncWindow::slotDelCmbRow() {
2160 int nRows = _cmbTable->rowCount();
2161 bool flg[nRows];
2162 for (int iRow = 0; iRow < nRows; iRow++) {
2163 if (_cmbTable->isItemSelected(_cmbTable->item(iRow,1))) {
2164 flg[iRow] = true;
2165 }
2166 else {
2167 flg[iRow] = false;
2168 }
2169 }
2170 for (int iRow = nRows-1; iRow >= 0; iRow--) {
2171 if (flg[iRow]) {
2172 _cmbTable->removeRow(iRow);
2173 }
2174 }
2175 nRows = _cmbTable->rowCount();
2176 if (nRows < 1) {
2177 enableWidget(false, _cmbMethodComboBox);
2178 _cmbMaxresLineEdit->setStyleSheet("background-color: lightGray");
2179 _cmbMaxresLineEdit->setEnabled(false);
2180 _cmbSamplSpinBox->setEnabled(false);
2181 }
2182}
2183
2184//
2185////////////////////////////////////////////////////////////////////////////
2186void bncWindow::populateCmbTable() {
2187
2188 for (int iRow = _cmbTable->rowCount()-1; iRow >=0; iRow--) {
2189 _cmbTable->removeRow(iRow);
2190 }
2191
2192 bncSettings settings;
2193
2194 int iRow = -1;
2195 QListIterator<QString> it(settings.value("combineStreams").toStringList());
2196 while (it.hasNext()) {
2197 QStringList hlp = it.next().split(" ");
2198 if (hlp.size() > 2) {
2199 ++iRow;
2200 _cmbTable->insertRow(iRow);
2201 }
2202 for (int iCol = 0; iCol < hlp.size(); iCol++) {
2203 _cmbTable->setItem(iRow, iCol, new QTableWidgetItem(hlp[iCol]));
2204 }
2205 }
2206}
2207
2208//
2209////////////////////////////////////////////////////////////////////////////
2210void bncWindow::slotAddUploadRow() {
2211 int iRow = _uploadTable->rowCount();
2212 _uploadTable->insertRow(iRow);
2213 for (int iCol = 0; iCol < _uploadTable->columnCount(); iCol++) {
2214 if (iCol == 3) {
2215 QLineEdit* passwd = new QLineEdit();
2216 passwd->setFrame(false);
2217 passwd->setEchoMode(QLineEdit::PasswordEchoOnEdit);
2218 _uploadTable->setCellWidget(iRow, iCol, passwd);
2219 }
2220 else if (iCol == 4) {
2221 QComboBox* system = new QComboBox();
2222 system->setEditable(false);
2223 system->addItems(QString(",IGS08,ETRF2000,NAD83,GDA94,SIRGAS95,SIRGAS2000,DREF91,Custom").split(","));
2224 system->setFrame(false);
2225 _uploadTable->setCellWidget(iRow, iCol, system);
2226 }
2227 else if (iCol == 5) {
2228 QCheckBox* com = new QCheckBox();
2229 _uploadTable->setCellWidget(iRow, iCol, com);
2230 }
2231 else if (iCol == 11) {
2232 bncTableItem* bncIt = new bncTableItem();
2233 bncIt->setFlags(bncIt->flags() & ~Qt::ItemIsEditable);
2234 _uploadTable->setItem(iRow, iCol, bncIt);
2235 BNC_CORE->_uploadTableItems[iRow] = bncIt;
2236 }
2237 else {
2238 _uploadTable->setItem(iRow, iCol, new QTableWidgetItem(""));
2239 }
2240 }
2241}
2242
2243//
2244////////////////////////////////////////////////////////////////////////////
2245void bncWindow::slotDelUploadRow() {
2246 BNC_CORE->_uploadTableItems.clear();
2247 int nRows = _uploadTable->rowCount();
2248 bool flg[nRows];
2249 for (int iRow = 0; iRow < nRows; iRow++) {
2250 if (_uploadTable->isItemSelected(_uploadTable->item(iRow,1))) {
2251 flg[iRow] = true;
2252 }
2253 else {
2254 flg[iRow] = false;
2255 }
2256 }
2257 for (int iRow = nRows-1; iRow >= 0; iRow--) {
2258 if (flg[iRow]) {
2259 _uploadTable->removeRow(iRow);
2260 }
2261 }
2262 for (int iRow = 0; iRow < _uploadTable->rowCount(); iRow++) {
2263 BNC_CORE->_uploadTableItems[iRow] =
2264 (bncTableItem*) _uploadTable->item(iRow, 11);
2265 }
2266 nRows = _uploadTable->rowCount();
2267 if (nRows < 1) {
2268 enableWidget(false, _uploadIntrComboBox);
2269 enableWidget(false, _uploadSamplRtcmEphCorrSpinBox);
2270 enableWidget(false, _uploadSamplSp3SpinBox);
2271 enableWidget(false, _uploadSamplClkRnxSpinBox);
2272 }
2273}
2274
2275//
2276////////////////////////////////////////////////////////////////////////////
2277void bncWindow::populateUploadTable() {
2278 for (int iRow = _uploadTable->rowCount()-1; iRow >=0; iRow--) {
2279 _uploadTable->removeRow(iRow);
2280 }
2281
2282 bncSettings settings;
2283
2284 int iRow = -1;
2285 QListIterator<QString> it(settings.value("uploadMountpointsOut").toStringList());
2286 while (it.hasNext()) {
2287 QStringList hlp = it.next().split(",");
2288 if (hlp.size() > 6) {
2289 ++iRow;
2290 _uploadTable->insertRow(iRow);
2291 }
2292 for (int iCol = 0; iCol < hlp.size(); iCol++) {
2293 if (iCol == 3) {
2294 QLineEdit* passwd = new QLineEdit();
2295 passwd->setFrame(false);
2296 passwd->setEchoMode(QLineEdit::PasswordEchoOnEdit);
2297 passwd->setText(hlp[iCol]);
2298 _uploadTable->setCellWidget(iRow, iCol, passwd);
2299 }
2300 else if (iCol == 4) {
2301 QComboBox* system = new QComboBox();
2302 system->setEditable(false);
2303 system->addItems(QString(",IGS08,ETRF2000,NAD83,GDA94,SIRGAS95,SIRGAS2000,DREF91,Custom").split(","));
2304 system->setFrame(false);
2305 system->setCurrentIndex(system->findText(hlp[iCol]));
2306 _uploadTable->setCellWidget(iRow, iCol, system);
2307 }
2308 else if (iCol == 5) {
2309 QCheckBox* com = new QCheckBox();
2310 if (hlp[iCol].toInt() == Qt::Checked) {
2311 com->setCheckState(Qt::Checked);
2312 }
2313 _uploadTable->setCellWidget(iRow, iCol, com);
2314 }
2315 else if (iCol == 11) {
2316 bncTableItem* bncIt = new bncTableItem();
2317 bncIt->setFlags(bncIt->flags() & ~Qt::ItemIsEditable);
2318 _uploadTable->setItem(iRow, iCol, bncIt);
2319 BNC_CORE->_uploadTableItems[iRow] = bncIt;
2320 }
2321 else {
2322 _uploadTable->setItem(iRow, iCol, new QTableWidgetItem(hlp[iCol]));
2323 }
2324 }
2325 }
2326}
2327
2328//
2329////////////////////////////////////////////////////////////////////////////
2330void bncWindow::slotSetUploadTrafo() {
2331 bncCustomTrafo* dlg = new bncCustomTrafo(this);
2332 dlg->exec();
2333 delete dlg;
2334}
2335
2336// Progress Bar Change
2337////////////////////////////////////////////////////////////////////////////
2338void bncWindow::slotPostProgress(int nEpo) {
2339 if (_actStart) {
2340 _actStart->setText(QString("%1 Epochs").arg(nEpo));
2341 }
2342}
2343
2344// Start Post-Processing Reqc
2345////////////////////////////////////////////////////////////////////////////
2346void bncWindow::startPostProcessingReqc() {
2347 _runningPostProcessingReqc = true;
2348 enableStartStop();
2349 if (_reqcActionComboBox->currentText() == "Analyze") {
2350 t_reqcAnalyze* reqcAnalyze = new t_reqcAnalyze(this);
2351 connect(reqcAnalyze, SIGNAL(finished()),
2352 this, SLOT(slotFinishedPostProcessingReqc()));
2353 reqcAnalyze->start();
2354 }
2355 else {
2356 t_reqcEdit* reqcEdit = new t_reqcEdit(this);
2357 connect(reqcEdit, SIGNAL(finished()),
2358 this, SLOT(slotFinishedPostProcessingReqc()));
2359 reqcEdit->start();
2360 }
2361}
2362
2363// Post-Processing Reqc Finished
2364////////////////////////////////////////////////////////////////////////////
2365void bncWindow::slotFinishedPostProcessingReqc() {
2366 _runningPostProcessingReqc = false;
2367 if (_reqcActionComboBox->currentText() != "Analyze") {
2368 QMessageBox::information(this, "Information",
2369 "RINEX Processing Thread Finished");
2370 }
2371 enableStartStop();
2372}
2373
2374//
2375////////////////////////////////////////////////////////////////////////////
2376void bncWindow::slotFinishedRnxPPP() {
2377 if (_actStart) {
2378 _actStart->setText(tr("Sta&rt"));
2379 }
2380}
2381
2382// Edit teqc-like editing options
2383////////////////////////////////////////////////////////////////////////////
2384void bncWindow::slotReqcEditOption() {
2385 reqcDlg* dlg = new reqcDlg(this);
2386 dlg->move(this->pos().x()+50, this->pos().y()+50);
2387 dlg->exec();
2388 delete dlg;
2389}
2390
2391// Enable/Disable Start and Stop Buttons
2392////////////////////////////////////////////////////////////////////////////
2393void bncWindow::enableStartStop() {
2394
2395 if ( _reqcActionComboBox && !_reqcActionComboBox->currentText().isEmpty() ) {
2396 if (_runningPostProcessingReqc) {
2397 _actStart->setEnabled(false);
2398 }
2399 else {
2400 _actStart->setEnabled(true);
2401 }
2402 _actStop->setEnabled(false);
2403 }
2404 else {
2405 if (_runningRealTime) {
2406 _actStart->setEnabled(false);
2407 _actStop->setEnabled(true);
2408 }
2409 else {
2410 _actStop->setEnabled(false);
2411 if (_mountPointsTable->rowCount() == 0) {
2412 _actStart->setEnabled(false);
2413 }
2414 else {
2415 _actStart->setEnabled(true);
2416 }
2417 }
2418 }
2419}
2420
2421// Show Map
2422////////////////////////////////////////////////////////////////////////////
2423void bncWindow::slotMapMountPoints() {
2424 saveOptions();
2425 t_bncMap* bncMap = new t_bncMap(this);
2426 bncMap->setMinimumSize(800, 600);
2427 bncMap->setWindowTitle("Selected Mountpoints");
2428
2429 bncSettings settings;
2430 QListIterator<QString> it(settings.value("mountPoints").toStringList());
2431 while (it.hasNext()) {
2432 QStringList hlp = it.next().split(" ");
2433 if (hlp.size() < 5) continue;
2434 QUrl url(hlp[0]);
2435 double latDeg = hlp[2].toDouble();
2436 double lonDeg = hlp[3].toDouble();
2437 bncMap->slotNewPoint(QFileInfo(url.path()).fileName(), latDeg, lonDeg);
2438 }
2439
2440 bncMap->show();
2441}
2442
2443// Show Map
2444////////////////////////////////////////////////////////////////////////////
2445void bncWindow::slotMapPPP() {
2446#ifdef QT_WEBKIT
2447 saveOptions();
2448 enableWidget(false, _pppWidgets._mapWinButton);
2449 enableWidget(false, _pppWidgets._useGoogleMap);
2450 enableWidget(false, _pppWidgets._useOpenStreetMap);
2451 enableWidget(false, _pppWidgets._mapWinDotSize);
2452 enableWidget(false, _pppWidgets._mapWinDotColor);
2453
2454 if (!_mapWin) {
2455 _mapWin = new bncMapWin(this);
2456 connect(_mapWin, SIGNAL(mapClosed()), this, SLOT(slotMapPPPClosed()));
2457 connect(BNC_CORE, SIGNAL(newPosition(QByteArray, bncTime, QVector<double>)),
2458 _mapWin, SLOT(slotNewPosition(QByteArray, bncTime, QVector<double>)));
2459 }
2460 _mapWin->show();
2461#else
2462 QMessageBox::information(this, "Information",
2463 "Qt Library compiled without QtWebKit");
2464#endif
2465}
2466
2467// Show Map
2468////////////////////////////////////////////////////////////////////////////
2469void bncWindow::slotMapPPPClosed() {
2470#ifdef QT_WEBKIT
2471 enableWidget(true, _pppWidgets._mapWinButton);
2472 enableWidget(true, _pppWidgets._useGoogleMap);
2473 enableWidget(true, _pppWidgets._useOpenStreetMap);
2474 enableWidget(true, _pppWidgets._mapWinDotSize);
2475 enableWidget(true, _pppWidgets._mapWinDotColor);
2476 if (_mapWin) {
2477 QListIterator<bncGetThread*> it(_threads);
2478 while (it.hasNext()) {
2479 bncGetThread* thread = it.next();
2480 thread->disconnect(_mapWin);
2481 }
2482 _mapWin->deleteLater();
2483 _mapWin = 0;
2484 }
2485#endif
2486}
Note: See TracBrowser for help on using the repository browser.