source: ntrip/trunk/BNC/bncwindow.cpp@ 885

Last change on this file since 885 was 841, checked in by weber, 17 years ago

* empty log message *

File size: 41.0 KB
RevLine 
[280]1// Part of BNC, a utility for retrieving decoding and
[464]2// converting GNSS data streams from NTRIP broadcasters.
[280]3//
[464]4// Copyright (C) 2007
[280]5// German Federal Agency for Cartography and Geodesy (BKG)
6// http://www.bkg.bund.de
[464]7// Czech Technical University Prague, Department of Geodesy
[280]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.
[35]24
25/* -------------------------------------------------------------------------
[93]26 * BKG NTRIP Client
[35]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
[274]41#include <unistd.h>
[35]42#include "bncwindow.h"
[149]43#include "bncapp.h"
[35]44#include "bncgetthread.h"
45#include "bnctabledlg.h"
[177]46#include "bnchlpdlg.h"
[168]47#include "bnchtml.h"
[366]48#include "bnctableitem.h"
[35]49
50using namespace std;
51
52// Constructor
53////////////////////////////////////////////////////////////////////////////
54bncWindow::bncWindow() {
55
[609]56 _caster = 0;
57
[91]58 int ww = QFontMetrics(this->font()).width('w');
[199]59
[410]60 static const QStringList labels = QString("account,mountpoint,decoder,lat,long,nmea,bytes").split(",");
[35]61
[372]62 setMinimumSize(77*ww, 65*ww);
[375]63
[841]64 setWindowTitle(tr("BKG Ntrip Client (BNC) Version 1.6"));
[91]65
[35]66 // Create Actions
67 // --------------
[108]68 _actHelp = new QAction(tr("&Help Contents"),this);
69 connect(_actHelp, SIGNAL(triggered()), SLOT(slotHelp()));
[35]70
[108]71 _actAbout = new QAction(tr("&About BNC"),this);
72 connect(_actAbout, SIGNAL(triggered()), SLOT(slotAbout()));
73
[261]74 _actFontSel = new QAction(tr("Select &Font"),this);
[110]75 connect(_actFontSel, SIGNAL(triggered()), SLOT(slotFontSel()));
76
[35]77 _actSaveOpt = new QAction(tr("&Save Options"),this);
78 connect(_actSaveOpt, SIGNAL(triggered()), SLOT(slotSaveOptions()));
79
80 _actQuit = new QAction(tr("&Quit"),this);
81 connect(_actQuit, SIGNAL(triggered()), SLOT(close()));
82
[182]83 _actAddMountPoints = new QAction(tr("Add &Mountpoints"),this);
[35]84 connect(_actAddMountPoints, SIGNAL(triggered()), SLOT(slotAddMountPoints()));
85
[105]86 _actDeleteMountPoints = new QAction(tr("&Delete Mountpoints"),this);
[35]87 connect(_actDeleteMountPoints, SIGNAL(triggered()), SLOT(slotDeleteMountPoints()));
[83]88 _actDeleteMountPoints->setEnabled(false);
[35]89
[182]90 _actGetData = new QAction(tr("Sta&rt"),this);
[35]91 connect(_actGetData, SIGNAL(triggered()), SLOT(slotGetData()));
92
[182]93 _actStop = new QAction(tr("Sto&p"),this);
94 connect(_actStop, SIGNAL(triggered()), SLOT(slotStop()));
95 _actStop->setEnabled(false);
96
[402]97 _actwhatsthis= new QAction(tr("Help=Shift+F1"),this);
[399]98 connect(_actwhatsthis, SIGNAL(triggered()), SLOT(slotWhatsThis()));
99
[679]100 CreateMenu();
101 AddToolbar();
[35]102
103 QSettings settings;
104 _proxyHostLineEdit = new QLineEdit(settings.value("proxyHost").toString());
105 _proxyPortLineEdit = new QLineEdit(settings.value("proxyPort").toString());
[83]106 _proxyPortLineEdit->setMaximumWidth(9*ww);
[135]107 _waitTimeSpinBox = new QSpinBox();
[139]108 _waitTimeSpinBox->setMinimum(1);
[135]109 _waitTimeSpinBox->setMaximum(30);
110 _waitTimeSpinBox->setSingleStep(1);
111 _waitTimeSpinBox->setSuffix(" sec");
112 _waitTimeSpinBox->setMaximumWidth(9*ww);
113 _waitTimeSpinBox->setValue(settings.value("waitTime").toInt());
[35]114 _outFileLineEdit = new QLineEdit(settings.value("outFile").toString());
115 _outPortLineEdit = new QLineEdit(settings.value("outPort").toString());
[83]116 _outPortLineEdit->setMaximumWidth(9*ww);
[588]117 _outEphPortLineEdit = new QLineEdit(settings.value("outEphPort").toString());
118 _outEphPortLineEdit->setMaximumWidth(9*ww);
[83]119 _rnxPathLineEdit = new QLineEdit(settings.value("rnxPath").toString());
[533]120 _ephPathLineEdit = new QLineEdit(settings.value("ephPath").toString());
[565]121
[533]122 _rnxV3CheckBox = new QCheckBox();
123 _rnxV3CheckBox->setCheckState(Qt::CheckState(settings.value("rnxV3").toInt()));
124 _ephV3CheckBox = new QCheckBox();
125 _ephV3CheckBox->setCheckState(Qt::CheckState(settings.value("ephV3").toInt()));
[106]126 _rnxScrpLineEdit = new QLineEdit(settings.value("rnxScript").toString());
[83]127 _rnxSkelLineEdit = new QLineEdit(settings.value("rnxSkel").toString());
128 _rnxSkelLineEdit->setMaximumWidth(5*ww);
[259]129 _rnxAppendCheckBox = new QCheckBox();
130 _rnxAppendCheckBox->setCheckState(Qt::CheckState(
131 settings.value("rnxAppend").toInt()));
[106]132 _rnxIntrComboBox = new QComboBox();
133 _rnxIntrComboBox->setMaximumWidth(9*ww);
134 _rnxIntrComboBox->setEditable(false);
[405]135 _rnxIntrComboBox->addItems(QString("1 min,2 min,5 min,10 min,15 min,30 min,1 hour,1 day").split(","));
[106]136 int ii = _rnxIntrComboBox->findText(settings.value("rnxIntr").toString());
137 if (ii != -1) {
138 _rnxIntrComboBox->setCurrentIndex(ii);
139 }
[560]140 _ephIntrComboBox = new QComboBox();
141 _ephIntrComboBox->setMaximumWidth(9*ww);
142 _ephIntrComboBox->setEditable(false);
[647]143 _ephIntrComboBox->addItems(QString("1 min,2 min,5 min,10 min,15 min,30 min,1 hour,1 day").split(","));
[560]144 int jj = _ephIntrComboBox->findText(settings.value("ephIntr").toString());
145 if (jj != -1) {
146 _ephIntrComboBox->setCurrentIndex(jj);
147 }
[106]148 _rnxSamplSpinBox = new QSpinBox();
149 _rnxSamplSpinBox->setMinimum(0);
150 _rnxSamplSpinBox->setMaximum(60);
151 _rnxSamplSpinBox->setSingleStep(5);
[107]152 _rnxSamplSpinBox->setMaximumWidth(9*ww);
[106]153 _rnxSamplSpinBox->setValue(settings.value("rnxSampl").toInt());
[135]154 _rnxSamplSpinBox->setSuffix(" sec");
[740]155
156 _binSamplSpinBox = new QSpinBox();
157 _binSamplSpinBox->setMinimum(0);
158 _binSamplSpinBox->setMaximum(60);
159 _binSamplSpinBox->setSingleStep(5);
160 _binSamplSpinBox->setMaximumWidth(9*ww);
161 _binSamplSpinBox->setValue(settings.value("binSampl").toInt());
162 _binSamplSpinBox->setSuffix(" sec");
163
[686]164 _obsRateComboBox = new QComboBox();
165 _obsRateComboBox->setMaximumWidth(9*ww);
166 _obsRateComboBox->setEditable(false);
[692]167 _obsRateComboBox->addItems(QString(",0.1 Hz,0.2 Hz,0.5 Hz,1 Hz,5 Hz").split(","));
[686]168 int kk = _obsRateComboBox->findText(settings.value("obsRate").toString());
169 if (kk != -1) {
170 _obsRateComboBox->setCurrentIndex(kk);
171 }
[723]172 _makePauseCheckBox = new QCheckBox();
173 _makePauseCheckBox->setCheckState(Qt::CheckState(
174 settings.value("makePause").toInt()));
[668]175 _adviseRecoSpinBox = new QSpinBox();
176 _adviseRecoSpinBox->setMinimum(0);
177 _adviseRecoSpinBox->setMaximum(60);
178 _adviseRecoSpinBox->setSingleStep(1);
179 _adviseRecoSpinBox->setSuffix(" min");
180 _adviseRecoSpinBox->setMaximumWidth(9*ww);
[670]181 _adviseRecoSpinBox->setValue(settings.value("adviseReco").toInt());
[668]182 _adviseFailSpinBox = new QSpinBox();
183 _adviseFailSpinBox->setMinimum(0);
184 _adviseFailSpinBox->setMaximum(60);
185 _adviseFailSpinBox->setSingleStep(1);
186 _adviseFailSpinBox->setSuffix(" min");
187 _adviseFailSpinBox->setMaximumWidth(9*ww);
[670]188 _adviseFailSpinBox->setValue(settings.value("adviseFail").toInt());
[143]189 _logFileLineEdit = new QLineEdit(settings.value("logFile").toString());
[670]190 _adviseScriptLineEdit = new QLineEdit(settings.value("adviseScript").toString());
[709]191
[728]192 _perfIntrComboBox = new QComboBox();
193 _perfIntrComboBox->setMaximumWidth(9*ww);
194 _perfIntrComboBox->setEditable(false);
195 _perfIntrComboBox->addItems(QString(",1 min,5 min,15 min,1 hour,6 hours,1 day").split(","));
196 int ll = _perfIntrComboBox->findText(settings.value("perfIntr").toString());
[709]197 if (ll != -1) {
[728]198 _perfIntrComboBox->setCurrentIndex(ll);
[709]199 }
200
[366]201 _mountPointsTable = new QTableWidget(0,7);
[375]202
[679]203 _mountPointsTable->horizontalHeader()->resizeSection(1,34*ww);
[366]204 _mountPointsTable->horizontalHeader()->resizeSection(2,9*ww);
205 _mountPointsTable->horizontalHeader()->resizeSection(3,7*ww);
206 _mountPointsTable->horizontalHeader()->resizeSection(4,7*ww);
207 _mountPointsTable->horizontalHeader()->resizeSection(5,5*ww);
[199]208 _mountPointsTable->horizontalHeader()->setResizeMode(QHeaderView::Interactive);
[203]209 _mountPointsTable->horizontalHeader()->setStretchLastSection(true);
[199]210 _mountPointsTable->setHorizontalHeaderLabels(labels);
[115]211 _mountPointsTable->setGridStyle(Qt::NoPen);
212 _mountPointsTable->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
[35]213 _mountPointsTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
214 _mountPointsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
215 QListIterator<QString> it(settings.value("mountPoints").toStringList());
216 if (!it.hasNext()) {
217 _actGetData->setEnabled(false);
218 }
219 int iRow = 0;
220 while (it.hasNext()) {
[59]221 QStringList hlp = it.next().split(" ");
[464]222 if (hlp.size() < 5) continue;
[35]223 _mountPointsTable->insertRow(iRow);
[110]224
225 QUrl url(hlp[0]);
226
227 QString fullPath = url.host() + QString(":%1").arg(url.port()) + url.path();
[366]228 QString format(hlp[1]); QString latitude(hlp[2]); QString longitude(hlp[3]);
229 QString nmea(hlp[4]);
[110]230
231 QTableWidgetItem* it;
232 it = new QTableWidgetItem(url.userInfo());
[115]233 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
[110]234 _mountPointsTable->setItem(iRow, 0, it);
235
236 it = new QTableWidgetItem(fullPath);
[115]237 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
[110]238 _mountPointsTable->setItem(iRow, 1, it);
239
240 it = new QTableWidgetItem(format);
241 _mountPointsTable->setItem(iRow, 2, it);
[184]242
[410]243 if (nmea == "yes") {
[366]244 it = new QTableWidgetItem(latitude);
245 _mountPointsTable->setItem(iRow, 3, it);
246 it = new QTableWidgetItem(longitude);
247 _mountPointsTable->setItem(iRow, 4, it);
248 } else {
249 it = new QTableWidgetItem(latitude);
250 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
251 _mountPointsTable->setItem(iRow, 3, it);
252 it = new QTableWidgetItem(longitude);
253 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
254 _mountPointsTable->setItem(iRow, 4, it);
255 }
256
257 it = new QTableWidgetItem(nmea);
258 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
259 _mountPointsTable->setItem(iRow, 5, it);
260
[184]261 bncTableItem* bncIt = new bncTableItem();
[276]262 bncIt->setFlags(bncIt->flags() & ~Qt::ItemIsEditable);
[366]263 _mountPointsTable->setItem(iRow, 6, bncIt);
[184]264
[35]265 iRow++;
266 }
[111]267 _mountPointsTable->hideColumn(0);
[110]268 _mountPointsTable->sortItems(1);
[35]269
[83]270 connect(_mountPointsTable, SIGNAL(itemSelectionChanged()),
271 SLOT(slotSelectionChanged()));
[35]272
[399]273 _log = new QTextBrowser();
[83]274 _log->setReadOnly(true);
[35]275
[683]276 // WhatsThis
277 // ---------
[684]278 _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>"));
279 _proxyPortLineEdit->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>"));
[683]280 _waitTimeSpinBox->setWhatsThis(tr("<p>When feeding a real-time GNSS engine waiting for input epoch by epoch, BNC drops whatever is received later than 'Wait for full 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 engine or products.</p><p>Note that 'Wait for full epoch' does not effect the RINEX Observation file content. Observations received later than 'Wait for full epoch' seconds will still be included in the RINEX Observation files.</p>"));
281 _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."));
[743]282 _outPortLineEdit->setWhatsThis(tr("BNC can produce synchronized observations in binary format on your local host through an IP port. Specify a port number here to activate this function."));
[683]283 _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."));
284 _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."));
285 _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."));
[684]286 _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><p>The triggering event for calling the script or batch file is the end of a RINEX Observation file 'Interval'. If that is overridden by a stream outage, the triggering event is the stream reconnection.</p>"));
[683]287 _rnxSkelLineEdit->setWhatsThis(tr("<p>Whenever BNC starts generating RINEX Observation files (and then once every day at midnight), it first tries to retrieve information needed for RINEX headers from so-called public RINEX header skeleton files which are derived from sitelogs. However, sometimes public RINEX header skeleton files are not available, its contents is not up to date, or you need to put additional/optional records in the RINEX header.</p><p>For that 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. A file in the RINEX 'Directory' with the RINEX 'Skeleton extension' is interpreted by BNC as a personal RINEX header skeleton file for the corresponding stream.</p>"));
288 _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>"));
289 _rnxIntrComboBox->setWhatsThis(tr("<p>Select the length of the RINEX Observation file.</p>"));
290 _ephIntrComboBox->setWhatsThis(tr("<p>Select the length of the RINEX Navigation file.</p>"));
291 _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>"));
[740]292 _binSamplSpinBox->setWhatsThis(tr("<p>Select the Observation sampling interval in seconds. A value of zero '0' tells BNC to send/store all received epochs.</p>"));
[705]293 _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><p>An empty option field (default) means that you don't want an explicit information from BNC about stream outages and incoming streams that can not be decoded and that the special procedure for handling of corrupted streams is bypassed.</p>"));
[723]294 _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><p>Note that for using this function you need to specify the 'Observation rate'.</p>"));
295 _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><p>Note that for using this function you need to specify the 'Observation rate'.</p>"));
296 _makePauseCheckBox->setWhatsThis(tr("<p>In case of a continuously corrupted stream, the decoding process can be paused and decodings are then attempted again at decreasing rate till the stream hopefully recovers. Tick 'Pause' to activate this function.</p><p>Do not tick 'Pause' (default) in order to prevent BNC from making any decoding pause. Be aware that this may incur an unnecessary workload.</p><p>Note that this function is only effective if an 'Observation rate' is specified.</p>"));
[683]297 _logFileLineEdit->setWhatsThis(tr("Records of BNC's activities are shown in the Log section 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."));
[723]298 _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 can be configured to send an 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><p> Note that for using this function you need to specify the 'Observation rate'.</p>"));
[730]299 _perfIntrComboBox->setWhatsThis(tr("<p>BNC can average all latencies per stream over a certain period of GPS time. The resulting mean latencies are recorded in the Log file/section at the end of each 'Performance log' interval together with results of a statistical evaluation (approximate number of covered epochs, data gaps).</p><p>Select a 'Performance log' interval or select the empty option field if you do not want BNC to log latencies and statistical information.</p>"));
[683]300 _mountPointsTable->setWhatsThis(tr("<p>Streams selected for retrieval are listed in the 'Mountpoints' section. Clicking on 'Add Mountpoints' 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 'Mountpoints' list, highlight it by clicking on it and hit the 'Delete Mountpoints' button. You can also remove multiple mountpoints 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' and 'format-details' as given in the sourcetable. However, there might be cases where you need to override the automatic selection due to incorrect sourcetable for example. BNC allows users to manually select the required decoder 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 'RTIGS'.</p><p>In case you need to log the raw data as is, BNC allows users to by-pass its decoders and 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). To initiate these streams, an approximate rover position needs to be sent in NMEA GGA message to the NTRIP broadcaster. In return, a user-specific data stream is generated, typically by a Network-RTK software. This stream is customized to the exact latitude and longitude as shown in the 'lat' and 'long' columns under 'Mountpoints'. These VRS streams are indicated by a 'yes' in the 'nmea' column under 'Mountpoints' as well as in the sourcetable. The default 'lat' and 'long' values are taken from the sourcetable. However, in most cases you would probably want to change this according to your requirement. Double click on 'lat' and 'long' fields, enter the values you wish to send and then hit Enter. The format is in positive north latitude degrees (e.g. for northern hemisphere: 52.436, for southern hemisphere: -24.567) and eastern longitude degrees (e.g.: 358.872 or -1.128). Only mountpoints with a 'yes' in its 'nmea' column can be edited. The position should preferably be a point within the coverage of the network.</p>"));
[684]301 _log->setWhatsThis(tr("Records of BNC's activities are shown in the Log section. 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."));
[683]302 _ephV3CheckBox->setWhatsThis(tr("The default format for RINEX Navigation files containing Broadcast Ephemeris is RINEX Version 2.11. Select 'Version 3' if you want to save the ephemeris in RINEX Version 3 format."));
303 _rnxV3CheckBox->setWhatsThis(tr("The default format for RINEX Observation files is RINEX Version 2.11. Select 'Version 3' if you want to save the observations in RINEX Version 3 format."));
[375]304
[679]305 // Canvas with Editable Fields
306 // ---------------------------
307 _canvas = new QWidget;
308 setCentralWidget(_canvas);
[143]309
[679]310 QTabWidget* aogroup = new QTabWidget();
311 QWidget* pgroup = new QWidget();
312 QWidget* ggroup = new QWidget();
313 QWidget* sgroup = new QWidget();
314 QWidget* egroup = new QWidget();
315 QWidget* agroup = new QWidget();
316 QWidget* ogroup = new QWidget();
317 aogroup->addTab(pgroup,tr("Proxy"));
318 aogroup->addTab(ggroup,tr("General"));
[701]319 aogroup->addTab(ogroup,tr("RINEX Observations"));
320 aogroup->addTab(egroup,tr("RINEX Ephemeris"));
[679]321 aogroup->addTab(sgroup,tr("Synchronized Observations"));
[705]322 aogroup->addTab(agroup,tr("Monitor"));
[143]323
[679]324 QGridLayout* pLayout = new QGridLayout;
325 pLayout->setColumnMinimumWidth(0,12*ww);
326 pLayout->addWidget(new QLabel("Proxy host"),0,0, Qt::AlignLeft);
327 pLayout->addWidget(_proxyHostLineEdit,0, 1);
328 pLayout->addWidget(new QLabel("Proxy port"),1,0, Qt::AlignLeft);
329 pLayout->addWidget(_proxyPortLineEdit,1,1);
[681]330 pLayout->addWidget(new QLabel("Settings for the proxy in protected networks, leave the boxes blank if none."),2, 0, 1, 2, Qt::AlignLeft);
[679]331 pLayout->addWidget(new QLabel(" "),3,0);
332 pLayout->addWidget(new QLabel(" "),4,0);
333 pLayout->addWidget(new QLabel(" "),5,0);
334 pgroup->setLayout(pLayout);
335
336 QGridLayout* gLayout = new QGridLayout;
337 gLayout->setColumnMinimumWidth(0,12*ww);
338 gLayout->addWidget(new QLabel("Logfile (full path)"), 0,0);
339 gLayout->addWidget(_logFileLineEdit, 0,1);
340 gLayout->addWidget(new QLabel("Append files") ,1,0 );
341 gLayout->addWidget(_rnxAppendCheckBox, 1,1 );
342 gLayout->addWidget(new QLabel("General settings for logfile and file handling."),2, 0, 1, 2, Qt::AlignLeft);
343 gLayout->addWidget(new QLabel(" "),3,0);
344 gLayout->addWidget(new QLabel(" "),4,0);
345 gLayout->addWidget(new QLabel(" "),5,0);
346 ggroup->setLayout(gLayout);
[533]347
[679]348 QGridLayout* sLayout = new QGridLayout;
349 sLayout->setColumnMinimumWidth(0,12*ww);
350 sLayout->addWidget(new QLabel("Port"), 0, 0);
351 sLayout->addWidget(_outPortLineEdit, 0, 1);
352 sLayout->addWidget(new QLabel("Wait for full epoch"), 1, 0);
353 sLayout->addWidget(_waitTimeSpinBox, 1, 1);
354 sLayout->addWidget(new QLabel("File (full path)"), 2, 0);
355 sLayout->addWidget(_outFileLineEdit, 2, 1);
[740]356 sLayout->addWidget(new QLabel("Sampling"), 3, 0, Qt::AlignLeft);
357 sLayout->addWidget(_binSamplSpinBox, 3, 1, Qt::AlignLeft);
358 sLayout->addWidget(new QLabel("Output synchronized observations epoch by epoch."),4,0,1,2,Qt::AlignLeft);
[679]359 sLayout->addWidget(new QLabel(" "),5,0);
[740]360 sLayout->addWidget(new QLabel(" "),6,0);
[679]361 sgroup->setLayout(sLayout);
[143]362
[679]363 QGridLayout* eLayout = new QGridLayout;
364 eLayout->setColumnMinimumWidth(0,12*ww);
[681]365 eLayout->addWidget(new QLabel("Directory"), 0, 0);
366 eLayout->addWidget(_ephPathLineEdit, 0, 1);
367 eLayout->addWidget(new QLabel("Interval"), 1, 0);
368 eLayout->addWidget(_ephIntrComboBox, 1, 1);
369 eLayout->addWidget(new QLabel("Port"), 2, 0);
370 eLayout->addWidget(_outEphPortLineEdit, 2, 1);
[679]371 eLayout->addWidget(new QLabel("Version 3"), 3, 0);
372 eLayout->addWidget(_ephV3CheckBox, 3, 1);
[682]373 eLayout->addWidget(new QLabel("Saving RINEX ephemeris files and ephemeris output through IP port."),4,0,1,2,Qt::AlignLeft);
[679]374 eLayout->addWidget(new QLabel(" "),5,0);
375 egroup->setLayout(eLayout);
[560]376
[679]377 QGridLayout* aLayout = new QGridLayout;
378 aLayout->setColumnMinimumWidth(0,12*ww);
[723]379 aLayout->setColumnMinimumWidth(1,8*ww);
380 aLayout->setColumnMinimumWidth(2,12*ww);
381 aLayout->setColumnMinimumWidth(3,40*ww);
[693]382 aLayout->addWidget(new QLabel("Observation rate"), 0, 0);
[686]383 aLayout->addWidget(_obsRateComboBox, 0, 1);
384 aLayout->addWidget(new QLabel("Failure threshold"), 1, 0);
[681]385 aLayout->addWidget(_adviseFailSpinBox, 1, 1);
[686]386 aLayout->addWidget(new QLabel("Recovery threshold"), 2, 0);
[681]387 aLayout->addWidget(_adviseRecoSpinBox, 2, 1);
[723]388 aLayout->addWidget(new QLabel("Pause"), 2, 2, Qt::AlignRight);
389 aLayout->addWidget(_makePauseCheckBox, 2, 3, Qt::AlignLeft);
[681]390 aLayout->addWidget(new QLabel("Script (full path)"), 3, 0);
[723]391 aLayout->addWidget(_adviseScriptLineEdit, 3, 1,1,3);
[730]392 aLayout->addWidget(new QLabel("Performance log"), 4, 0);
[728]393 aLayout->addWidget(_perfIntrComboBox, 4, 1);
394 aLayout->addWidget(new QLabel("Network monitoring, outages, handling of corrupted streams, latencies, statistics."),5,0,1,4,Qt::AlignLeft);
[679]395 agroup->setLayout(aLayout);
[560]396
[679]397 QGridLayout* oLayout = new QGridLayout;
398 oLayout->setColumnMinimumWidth(0,12*ww);
399 oLayout->setColumnMinimumWidth(1,8*ww);
400 oLayout->setColumnMinimumWidth(2,12*ww);
401 oLayout->setColumnMinimumWidth(3,40*ww);
[681]402 oLayout->addWidget(new QLabel("Directory"), 0, 0);
403 oLayout->addWidget(_rnxPathLineEdit, 0, 1,1,3);
404 oLayout->addWidget(new QLabel("Interval"), 1, 0);
405 oLayout->addWidget(_rnxIntrComboBox, 1, 1);
406 oLayout->addWidget(new QLabel("Sampling"), 1, 2, Qt::AlignRight);
407 oLayout->addWidget(_rnxSamplSpinBox, 1, 3, Qt::AlignLeft);
[682]408 oLayout->addWidget(new QLabel("Skeleton extension"), 2, 0);
[679]409 oLayout->addWidget(_rnxSkelLineEdit, 2, 1);
410 oLayout->addWidget(new QLabel("Script (full path)"), 3, 0);
411 oLayout->addWidget(_rnxScrpLineEdit, 3, 1, 1, 3);
412 oLayout->addWidget(new QLabel("Version 3"), 4, 0);
413 oLayout->addWidget(_rnxV3CheckBox, 4, 1);
414 oLayout->addWidget(new QLabel("Saving RINEX observation files."),5,0,1,4, Qt::AlignLeft);
415 ogroup->setLayout(oLayout);
416
417 QVBoxLayout* mLayout = new QVBoxLayout;
418 mLayout->addWidget(aogroup);
419 mLayout->addWidget(_mountPointsTable);
420 mLayout->addWidget(_log);
[106]421
[679]422 _canvas->setLayout(mLayout);
[35]423}
424
425// Destructor
426////////////////////////////////////////////////////////////////////////////
427bncWindow::~bncWindow() {
[609]428 delete _caster;
[35]429}
430
431// Retrieve Table
432////////////////////////////////////////////////////////////////////////////
433void bncWindow::slotAddMountPoints() {
[101]434
435 QSettings settings;
436 QString proxyHost = settings.value("proxyHost").toString();
437 int proxyPort = settings.value("proxyPort").toInt();
438 if (proxyHost != _proxyHostLineEdit->text() ||
439 proxyPort != _proxyPortLineEdit->text().toInt()) {
[102]440 int iRet = QMessageBox::question(this, "Question", "Proxy options "
[101]441 "changed. Use the new ones?",
442 QMessageBox::Yes, QMessageBox::No,
443 QMessageBox::NoButton);
444 if (iRet == QMessageBox::Yes) {
445 settings.setValue("proxyHost", _proxyHostLineEdit->text());
446 settings.setValue("proxyPort", _proxyPortLineEdit->text());
447 }
448 }
449
[90]450 bncTableDlg* dlg = new bncTableDlg(this);
[83]451 dlg->move(this->pos().x()+50, this->pos().y()+50);
[35]452 connect(dlg, SIGNAL(newMountPoints(QStringList*)),
453 this, SLOT(slotNewMountPoints(QStringList*)));
454 dlg->exec();
455 delete dlg;
456
457}
458
459// Delete Selected Mount Points
460////////////////////////////////////////////////////////////////////////////
461void bncWindow::slotDeleteMountPoints() {
[117]462
463 int nRows = _mountPointsTable->rowCount();
464 bool flg[nRows];
465 for (int iRow = 0; iRow < nRows; iRow++) {
[116]466 if (_mountPointsTable->isItemSelected(_mountPointsTable->item(iRow,1))) {
[117]467 flg[iRow] = true;
468 }
469 else {
470 flg[iRow] = false;
471 }
472 }
473 for (int iRow = nRows-1; iRow >= 0; iRow--) {
474 if (flg[iRow]) {
[116]475 _mountPointsTable->removeRow(iRow);
[83]476 }
477 }
478 _actDeleteMountPoints->setEnabled(false);
[183]479
480 if (_mountPointsTable->rowCount() == 0) {
481 _actGetData->setEnabled(false);
482 }
[35]483}
484
485// New Mount Points Selected
486////////////////////////////////////////////////////////////////////////////
487void bncWindow::slotNewMountPoints(QStringList* mountPoints) {
488 int iRow = 0;
489 QListIterator<QString> it(*mountPoints);
490 while (it.hasNext()) {
[59]491 QStringList hlp = it.next().split(" ");
[110]492 QUrl url(hlp[0]);
493 QString fullPath = url.host() + QString(":%1").arg(url.port()) + url.path();
[366]494 QString format(hlp[1]); QString latitude(hlp[2]); QString longitude(hlp[3]);
495 QString nmea(hlp[4]);
[110]496
[35]497 _mountPointsTable->insertRow(iRow);
[110]498
499 QTableWidgetItem* it;
500 it = new QTableWidgetItem(url.userInfo());
[115]501 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
[110]502 _mountPointsTable->setItem(iRow, 0, it);
503
504 it = new QTableWidgetItem(fullPath);
[115]505 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
[110]506 _mountPointsTable->setItem(iRow, 1, it);
507
508 it = new QTableWidgetItem(format);
509 _mountPointsTable->setItem(iRow, 2, it);
[184]510
[410]511 if (nmea == "yes") {
[366]512 it = new QTableWidgetItem(latitude);
513 _mountPointsTable->setItem(iRow, 3, it);
514 it = new QTableWidgetItem(longitude);
515 _mountPointsTable->setItem(iRow, 4, it);
516 } else {
517 it = new QTableWidgetItem(latitude);
518 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
519 _mountPointsTable->setItem(iRow, 3, it);
520 it = new QTableWidgetItem(longitude);
521 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
522 _mountPointsTable->setItem(iRow, 4, it);
523 }
524
525 it = new QTableWidgetItem(nmea);
526 it->setFlags(it->flags() & ~Qt::ItemIsEditable);
527 _mountPointsTable->setItem(iRow, 5, it);
528
[184]529 bncTableItem* bncIt = new bncTableItem();
[366]530 _mountPointsTable->setItem(iRow, 6, bncIt);
[184]531
[35]532 iRow++;
533 }
[111]534 _mountPointsTable->hideColumn(0);
[110]535 _mountPointsTable->sortItems(1);
[35]536 if (mountPoints->count() > 0) {
537 _actGetData->setEnabled(true);
538 }
539 delete mountPoints;
540}
541
542// Save Options
543////////////////////////////////////////////////////////////////////////////
544void bncWindow::slotSaveOptions() {
545 QSettings settings;
546 settings.setValue("proxyHost", _proxyHostLineEdit->text());
547 settings.setValue("proxyPort", _proxyPortLineEdit->text());
[135]548 settings.setValue("waitTime", _waitTimeSpinBox->value());
[686]549 settings.setValue("obsRate", _obsRateComboBox->currentText());
[668]550 settings.setValue("adviseFail", _adviseFailSpinBox->value());
551 settings.setValue("adviseReco", _adviseRecoSpinBox->value());
[723]552 settings.setValue("makePause", _makePauseCheckBox->checkState());
[35]553 settings.setValue("outFile", _outFileLineEdit->text());
[728]554 settings.setValue("perfIntr", _perfIntrComboBox->currentText());
[35]555 settings.setValue("outPort", _outPortLineEdit->text());
[588]556 settings.setValue("outEphPort", _outEphPortLineEdit->text());
[83]557 settings.setValue("rnxPath", _rnxPathLineEdit->text());
[533]558 settings.setValue("ephPath", _ephPathLineEdit->text());
[106]559 settings.setValue("rnxScript", _rnxScrpLineEdit->text());
560 settings.setValue("rnxIntr", _rnxIntrComboBox->currentText());
[560]561 settings.setValue("ephIntr", _ephIntrComboBox->currentText());
[106]562 settings.setValue("rnxSampl", _rnxSamplSpinBox->value());
[740]563 settings.setValue("binSampl", _binSamplSpinBox->value());
[83]564 settings.setValue("rnxSkel", _rnxSkelLineEdit->text());
[259]565 settings.setValue("rnxAppend", _rnxAppendCheckBox->checkState());
[533]566 settings.setValue("rnxV3", _rnxV3CheckBox->checkState());
567 settings.setValue("ephV3", _ephV3CheckBox->checkState());
[143]568 settings.setValue("logFile", _logFileLineEdit->text());
[668]569 settings.setValue("adviseScript",_adviseScriptLineEdit->text());
[356]570
571QStringList mountPoints;
[110]572
[35]573 for (int iRow = 0; iRow < _mountPointsTable->rowCount(); iRow++) {
[112]574 QUrl url( "//" + _mountPointsTable->item(iRow, 0)->text() +
575 "@" + _mountPointsTable->item(iRow, 1)->text() );
[110]576
577 mountPoints.append(url.toString() + " " +
[366]578 _mountPointsTable->item(iRow, 2)->text()
579 + " " + _mountPointsTable->item(iRow, 3)->text()
580 + " " + _mountPointsTable->item(iRow, 4)->text()
581 + " " + _mountPointsTable->item(iRow, 5)->text());
[35]582 }
583 settings.setValue("mountPoints", mountPoints);
584}
585
586// All get slots terminated
587////////////////////////////////////////////////////////////////////////////
588void bncWindow::slotGetThreadErrors() {
[83]589 slotMessage("All Get Threads Terminated");
[149]590 ((bncApp*)qApp)->slotMessage("All Get Threads Terminated");
[35]591 _actAddMountPoints->setEnabled(true);
592 _actGetData->setEnabled(true);
593}
594
595// Retrieve Data
596////////////////////////////////////////////////////////////////////////////
597void bncWindow::slotGetData() {
[128]598 slotSaveOptions();
599
[35]600 _actAddMountPoints->setEnabled(false);
601 _actDeleteMountPoints->setEnabled(false);
602 _actGetData->setEnabled(false);
[182]603 _actStop->setEnabled(true);
[35]604
[463]605 _caster = new bncCaster(_outFileLineEdit->text(),
[592]606 _outPortLineEdit->text().toInt());
[35]607
[592]608 ((bncApp*)qApp)->setPort(_outEphPortLineEdit->text().toInt());
609
[463]610 connect(_caster, SIGNAL(getThreadErrors()),
[35]611 this, SLOT(slotGetThreadErrors()));
612
[628]613 connect(_caster, SIGNAL(newMessage(QByteArray)),
614 this, SLOT(slotMessage(QByteArray)));
615 connect(_caster, SIGNAL(newMessage(QByteArray)),
616 (bncApp*)qApp, SLOT(slotMessage(QByteArray)));
[83]617
[295]618 slotMessage("============ Start BNC ============");
619 ((bncApp*)qApp)->slotMessage("============ Start BNC ============");
620
[35]621 for (int iRow = 0; iRow < _mountPointsTable->rowCount(); iRow++) {
[112]622 QUrl url( "//" + _mountPointsTable->item(iRow, 0)->text() +
623 "@" + _mountPointsTable->item(iRow, 1)->text() );
[110]624
[111]625 QByteArray format = _mountPointsTable->item(iRow, 2)->text().toAscii();
[35]626
[366]627 QByteArray latitude = _mountPointsTable->item(iRow, 3)->text().toAscii();
628 QByteArray longitude = _mountPointsTable->item(iRow, 4)->text().toAscii();
629 QByteArray nmea = _mountPointsTable->item(iRow, 5)->text().toAscii();
[83]630
[366]631 bncGetThread* getThread = new bncGetThread(url, format, latitude, longitude, nmea, iRow);
632
[628]633 connect(getThread, SIGNAL(newMessage(QByteArray)),
634 this, SLOT(slotMessage(QByteArray)));
635 connect(getThread, SIGNAL(newMessage(QByteArray)),
636 (bncApp*)qApp, SLOT(slotMessage(QByteArray)));
[83]637
[628]638 connect(getThread, SIGNAL(newBytes(QByteArray, double)),
[366]639 (bncTableItem*) _mountPointsTable->item(iRow, 6),
[628]640 SLOT(slotNewBytes(QByteArray, double)));
[184]641
[463]642 _caster->addGetThread(getThread);
[35]643
644 getThread->start();
645 }
646}
[83]647
[182]648// Retrieve Data
649////////////////////////////////////////////////////////////////////////////
650void bncWindow::slotStop() {
651 int iRet = QMessageBox::question(this, "Stop", "Stop retrieving data?",
652 QMessageBox::Yes, QMessageBox::No,
653 QMessageBox::NoButton);
654 if (iRet == QMessageBox::Yes) {
[463]655 delete _caster; _caster = 0;
[182]656 _actGetData->setEnabled(true);
657 _actStop->setEnabled(false);
[183]658 _actAddMountPoints->setEnabled(true);
[182]659 }
660}
661
[83]662// Close Application gracefully
663////////////////////////////////////////////////////////////////////////////
664void bncWindow::closeEvent(QCloseEvent* event) {
665
666 int iRet = QMessageBox::question(this, "Close", "Save Options?",
667 QMessageBox::Yes, QMessageBox::No,
668 QMessageBox::Cancel);
669
670 if (iRet == QMessageBox::Cancel) {
671 event->ignore();
672 return;
673 }
674 else if (iRet == QMessageBox::Yes) {
675 slotSaveOptions();
676 }
677
[608]678 QMainWindow::closeEvent(event);
679 delete this;
[83]680}
681
682// User changed the selection of mountPoints
683////////////////////////////////////////////////////////////////////////////
684void bncWindow::slotSelectionChanged() {
685 if (_mountPointsTable->selectedItems().isEmpty()) {
686 _actDeleteMountPoints->setEnabled(false);
687 }
688 else {
689 _actDeleteMountPoints->setEnabled(true);
690 }
691}
692
693// Display Program Messages
694////////////////////////////////////////////////////////////////////////////
[458]695void bncWindow::slotMessage(const QByteArray msg) {
[83]696
697 const int maxBufferSize = 10000;
698
[189]699 QString txt = _log->toPlainText() + "\n" +
[566]700 QDateTime::currentDateTime().toUTC().toString("yy-MM-dd hh:mm:ss ") + msg;
[83]701 _log->clear();
702 _log->append(txt.right(maxBufferSize));
703}
704
[108]705// About Message
706////////////////////////////////////////////////////////////////////////////
707void bncWindow::slotAbout() {
[679]708 new bncAboutDlg(0);
[108]709}
710
711// Help Window
712////////////////////////////////////////////////////////////////////////////
713void bncWindow::slotHelp() {
[177]714 QUrl url;
715 url.setPath(":bnchelp.html");
[676]716 new bncHlpDlg(0, url);
[108]717}
[110]718
719// Select Fonts
720////////////////////////////////////////////////////////////////////////////
721void bncWindow::slotFontSel() {
722 bool ok;
723 QFont newFont = QFontDialog::getFont(&ok, this->font(), this);
724 if (ok) {
[113]725 QSettings settings;
726 settings.setValue("font", newFont.toString());
[110]727 QApplication::setFont(newFont);
[113]728 int ww = QFontMetrics(newFont).width('w');
[152]729 setMinimumSize(60*ww, 80*ww);
730 resize(60*ww, 80*ww);
[110]731 }
732}
[399]733
734// Whats This Help
735void bncWindow::slotWhatsThis() {
736QWhatsThis::enterWhatsThisMode();
737}
738
[679]739void bncWindow::CreateMenu() {
740 // Create Menus
741 // ------------
742 _menuFile = menuBar()->addMenu(tr("&File"));
743 _menuFile->addAction(_actFontSel);
744 _menuFile->addSeparator();
745 _menuFile->addAction(_actSaveOpt);
746 _menuFile->addSeparator();
747 _menuFile->addAction(_actQuit);
[399]748
[679]749 _menuHlp = menuBar()->addMenu(tr("&Help"));
750 _menuHlp->addAction(_actHelp);
751 _menuHlp->addAction(_actAbout);
752
753}
754
755void bncWindow::AddToolbar() {
756 // Tool (Command) Bar
757 // ------------------
758 QToolBar* toolBar = new QToolBar;
759 addToolBar(Qt::BottomToolBarArea, toolBar);
760 toolBar->setMovable(false);
761 toolBar->addAction(_actAddMountPoints);
762 toolBar->addAction(_actDeleteMountPoints);
763 toolBar->addAction(_actGetData);
764 toolBar->addAction(_actStop);
765 toolBar->addWidget(new QLabel(" "));
766 toolBar->addAction(_actwhatsthis);
767}
768
769bncAboutDlg::bncAboutDlg(QWidget* parent) :
770 QDialog(parent) {
771
772 QTextBrowser* tb = new QTextBrowser;
773 QUrl url; url.setPath(":bncabout.html");
774 tb->setSource(url);
775 tb->setReadOnly(true);
776
777 int ww = QFontMetrics(font()).width('w');
778 QPushButton* _closeButton = new QPushButton("Close");
779 _closeButton->setMaximumWidth(10*ww);
780 connect(_closeButton, SIGNAL(clicked()), this, SLOT(close()));
781
782 QGridLayout* dlgLayout = new QGridLayout();
783 QLabel* img = new QLabel();
784 img->setPixmap(QPixmap(":ntrip-logo.png"));
785 dlgLayout->addWidget(img, 0,0);
[841]786 dlgLayout->addWidget(new QLabel("BKG NTRIP Client (BNC) Version 1.6"), 0,1);
[679]787 dlgLayout->addWidget(tb,1,0,1,2);
788 dlgLayout->addWidget(_closeButton,2,1,Qt::AlignRight);
789
790 setLayout(dlgLayout);
791 resize(60*ww, 60*ww);
792 show();
793}
794
795bncAboutDlg::~bncAboutDlg() {
796};
797
Note: See TracBrowser for help on using the repository browser.