QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
NTRIPManager.cc
Go to the documentation of this file.
1#include "NTRIPManager.h"
3#include "NTRIPSourceTable.h"
4#include "NTRIPSettings.h"
6#include "Fact.h"
7#include "FactGroup.h"
8#include "QGCApplication.h"
10#include "RTCMMavlink.h"
11#include "SettingsManager.h"
12
13#include "MultiVehicleManager.h"
14#include "PositionManager.h"
15#include "Vehicle.h"
16#include <QtPositioning/QGeoCoordinate>
17#include <QtCore/QDateTime>
18
19#include <QtCore/QApplicationStatic>
20#include <QtCore/QCoreApplication>
21#include <QtCore/QDebug>
22#include <QtCore/QtGlobal>
23
24QGC_LOGGING_CATEGORY(NTRIPManagerLog, "GPS.NTRIPManager")
25
26Q_APPLICATION_STATIC(NTRIPManager, _ntripManagerInstance);
27
28NTRIPManager* NTRIPManager::instance()
29{
30 return _ntripManagerInstance();
31}
32
33NTRIPManager::NTRIPManager(QObject* parent)
34 : QObject(parent)
35{
36 qCDebug(NTRIPManagerLog) << "NTRIPManager created";
37
38 _rtcmMavlink = qgcApp() ? qgcApp()->findChild<RTCMMavlink*>() : nullptr;
39 if (!_rtcmMavlink) {
40 QObject* parentObj = qgcApp() ? static_cast<QObject*>(qgcApp()) : static_cast<QObject*>(this);
41 _rtcmMavlink = new RTCMMavlink(parentObj);
42 _rtcmMavlink->setObjectName(QStringLiteral("RTCMMavlink"));
43 }
44
45 NTRIPSettings* settings = SettingsManager::instance()->ntripSettings();
46 if (settings) {
47 auto connectSetting = [this](Fact* fact) {
48 if (fact) {
49 connect(fact, &Fact::rawValueChanged, this, &NTRIPManager::_onSettingChanged);
50 }
51 };
52 connectSetting(settings->ntripServerConnectEnabled());
53 connectSetting(settings->ntripServerHostAddress());
54 connectSetting(settings->ntripServerPort());
55 connectSetting(settings->ntripUsername());
56 connectSetting(settings->ntripPassword());
57 connectSetting(settings->ntripMountpoint());
58 connectSetting(settings->ntripWhitelist());
59 connectSetting(settings->ntripUseTls());
60 connectSetting(settings->ntripUdpForwardEnabled());
61 connectSetting(settings->ntripUdpTargetAddress());
62 connectSetting(settings->ntripUdpTargetPort());
63 }
64
65 // Check initial state (handles connect-on-start)
66 QTimer::singleShot(0, this, &NTRIPManager::_onSettingChanged);
67
68 _ggaTimer = new QTimer(this);
69 _ggaTimer->setInterval(5000);
70 connect(_ggaTimer, &QTimer::timeout, this, &NTRIPManager::_sendGGA);
71
72 _dataRateTimer = new QTimer(this);
73 _dataRateTimer->setInterval(1000);
74 connect(_dataRateTimer, &QTimer::timeout, this, [this]() {
75 const double rate = static_cast<double>(_bytesReceived - _dataRatePrevBytes);
76 _dataRatePrevBytes = _bytesReceived;
77 if (!qFuzzyCompare(_dataRateBytesPerSec + 1.0, rate + 1.0)) {
78 _dataRateBytesPerSec = rate;
79 emit dataRateChanged();
80 }
81 emit bytesReceivedChanged();
82 emit messagesReceivedChanged();
83 });
84
85 connect(qApp, &QCoreApplication::aboutToQuit, this, &NTRIPManager::stopNTRIP, Qt::QueuedConnection);
86}
87
88NTRIPManager::~NTRIPManager()
89{
90 qCDebug(NTRIPManagerLog) << "NTRIPManager destroyed";
91 stopNTRIP();
92}
93
94void NTRIPManager::_setStatus(ConnectionStatus status, const QString& msg)
95{
96 bool changed = false;
97
98 if (_connectionStatus != status) {
99 _connectionStatus = status;
100 changed = true;
102 }
103
104 if (_statusMessage != msg) {
105 _statusMessage = msg;
106 changed = true;
108 }
109
110 if (changed) {
111 qCDebug(NTRIPManagerLog) << "NTRIP status:" << static_cast<int>(status) << msg;
112 }
113}
114
115NTRIPTransportConfig NTRIPManager::_configFromSettings() const
116{
118 NTRIPSettings* settings = SettingsManager::instance()->ntripSettings();
119 if (!settings) {
120 return config;
121 }
122
123 if (settings->ntripServerHostAddress()) config.host = settings->ntripServerHostAddress()->rawValue().toString();
124 if (settings->ntripServerPort()) config.port = settings->ntripServerPort()->rawValue().toInt();
125 if (settings->ntripUsername()) config.username = settings->ntripUsername()->rawValue().toString();
126 if (settings->ntripPassword()) config.password = settings->ntripPassword()->rawValue().toString();
127 if (settings->ntripMountpoint()) config.mountpoint = settings->ntripMountpoint()->rawValue().toString();
128 if (settings->ntripWhitelist()) config.whitelist = settings->ntripWhitelist()->rawValue().toString();
129 if (settings->ntripUseTls()) config.useTls = settings->ntripUseTls()->rawValue().toBool();
130
131 return config;
132}
133
134void NTRIPManager::startNTRIP()
135{
136 if (_startStopBusy || _transport) {
137 if (_startStopBusy) {
138 qCWarning(NTRIPManagerLog) << "startNTRIP called while start/stop in progress";
139 }
140 return;
141 }
142 _startStopBusy = true;
143
144 if (_reconnectTimer) {
145 _reconnectTimer->stop();
146 }
147
148 NTRIPTransportConfig config = _configFromSettings();
149
150 NTRIPSettings* settings = SettingsManager::instance()->ntripSettings();
151 if (settings && settings->ntripUdpForwardEnabled()) {
152 _udpForwardEnabled = settings->ntripUdpForwardEnabled()->rawValue().toBool();
153 }
154
155 if (_udpForwardEnabled && settings) {
156 const QString udpAddr = settings->ntripUdpTargetAddress()->rawValue().toString();
157 const quint16 udpPort = static_cast<quint16>(settings->ntripUdpTargetPort()->rawValue().toUInt());
158 const QHostAddress parsedAddr(udpAddr);
159 if (!udpAddr.isEmpty() && !parsedAddr.isNull() && udpPort > 0) {
160 _udpTargetAddress = parsedAddr;
161 _udpTargetPort = udpPort;
162 if (!_udpSocket) {
163 _udpSocket = new QUdpSocket(this);
164 }
165 qCDebug(NTRIPManagerLog) << "NTRIP UDP forwarding enabled:" << udpAddr << ":" << udpPort;
166 } else {
167 qCWarning(NTRIPManagerLog) << "NTRIP UDP forwarding enabled but address/port invalid";
168 _udpForwardEnabled = false;
169 }
170 }
171
172 if (config.host.isEmpty()) {
173 qCWarning(NTRIPManagerLog) << "NTRIP host address is empty";
174 _setStatus(ConnectionStatus::Error, tr("No host address"));
175 _startStopBusy = false;
176 return;
177 }
178
179 if (config.port <= 0 || config.port > 65535) {
180 qCWarning(NTRIPManagerLog) << "NTRIP port is invalid:" << config.port;
181 _setStatus(ConnectionStatus::Error, tr("Invalid port"));
182 _startStopBusy = false;
183 return;
184 }
185
186 qCDebug(NTRIPManagerLog) << "startNTRIP: host=" << config.host << " port=" << config.port
187 << " mount=" << config.mountpoint;
188
189 _setStatus(ConnectionStatus::Connecting, tr("Connecting to %1:%2...").arg(config.host).arg(config.port));
190
191 _bytesReceived = 0;
192 _messagesReceived = 0;
193 _dataRateBytesPerSec = 0.0;
194 _dataRatePrevBytes = 0;
197 emit dataRateChanged();
198
199 _runningConfig = config;
200 _runningUdpForward = _udpForwardEnabled;
201 _runningUdpAddr = _udpForwardEnabled ? _udpTargetAddress.toString() : QString();
202 _runningUdpPort = _udpForwardEnabled ? _udpTargetPort : 0;
203
204 _transport = new NTRIPHttpTransport(config, this);
205
206 connect(_transport, &NTRIPHttpTransport::error,
207 this, &NTRIPManager::_tcpError);
208
209 connect(_transport, &NTRIPHttpTransport::connected, this, [this]() {
210 _reconnectAttempts = 0;
211 _casterStatus = CasterStatus::CasterConnected;
212 emit casterStatusChanged(_casterStatus);
213
214 _setStatus(ConnectionStatus::Connected, tr("Connected"));
215
216 if (_ggaTimer && !_ggaTimer->isActive()) {
217 _ggaTimer->setInterval(1000);
218 _sendGGA();
219 _ggaTimer->start();
220 }
221 _dataRateTimer->start();
222 });
223
224 connect(_transport, &NTRIPHttpTransport::RTCMDataUpdate, this, &NTRIPManager::_rtcmDataReceived);
225
226 _transport->start();
227 qCDebug(NTRIPManagerLog) << "NTRIP started";
228
229 _startStopBusy = false;
230}
231
232void NTRIPManager::stopNTRIP()
233{
234 if (_startStopBusy) {
235 qCWarning(NTRIPManagerLog) << "stopNTRIP called while start/stop in progress";
236 return;
237 }
238 _startStopBusy = true;
239
240 if (_reconnectTimer) {
241 _reconnectTimer->stop();
242 }
243
244 if (_transport) {
245 disconnect(_transport, &NTRIPHttpTransport::error, this, &NTRIPManager::_tcpError);
246 _transport->stop();
247 _transport->deleteLater();
248 _transport = nullptr;
249
250 _runningConfig = {};
251 _runningUdpForward = false;
252 _runningUdpAddr.clear();
253 _runningUdpPort = 0;
254
255 _setStatus(ConnectionStatus::Disconnected, tr("Disconnected"));
256 qCDebug(NTRIPManagerLog) << "NTRIP stopped";
257 }
258
259 if (_ggaTimer && _ggaTimer->isActive()) {
260 _ggaTimer->stop();
261 }
262 _dataRateTimer->stop();
263 if (_dataRateBytesPerSec != 0.0) {
264 _dataRateBytesPerSec = 0.0;
265 _dataRatePrevBytes = 0;
266 emit dataRateChanged();
267 }
268
269 if (_udpSocket) {
270 _udpSocket->close();
271 delete _udpSocket;
272 _udpSocket = nullptr;
273 }
274 _udpForwardEnabled = false;
275
276 _startStopBusy = false;
277}
278
279void NTRIPManager::_tcpError(const QString& errorMsg)
280{
281 qCWarning(NTRIPManagerLog) << "NTRIP error:" << errorMsg;
282 _setStatus(ConnectionStatus::Error, errorMsg);
283
284 if (errorMsg.contains("NO_LOCATION_PROVIDED", Qt::CaseInsensitive)) {
285 _casterStatus = CasterStatus::CasterNoLocation;
286 } else {
287 _casterStatus = CasterStatus::CasterError;
288 }
289 emit casterStatusChanged(_casterStatus);
290
291 _scheduleReconnect();
292}
293
294void NTRIPManager::_scheduleReconnect()
295{
296 NTRIPSettings* settings = SettingsManager::instance()->ntripSettings();
297 bool shouldRun = settings && settings->ntripServerConnectEnabled() &&
298 settings->ntripServerConnectEnabled()->rawValue().toBool();
299 if (!shouldRun) {
300 return;
301 }
302
303 stopNTRIP();
304
305 int backoffMs = qMin(kMinReconnectMs * (1 << qMin(_reconnectAttempts, 4)), kMaxReconnectMs);
306 _reconnectAttempts = qMin(_reconnectAttempts + 1, kMaxReconnectAttempts);
307
308 qCDebug(NTRIPManagerLog) << "NTRIP reconnecting in" << backoffMs << "ms (attempt" << _reconnectAttempts << ")";
309
310 _setStatus(ConnectionStatus::Reconnecting, tr("Reconnecting in %1s...").arg(backoffMs / 1000));
311
312 if (!_reconnectTimer) {
313 _reconnectTimer = new QTimer(this);
314 _reconnectTimer->setSingleShot(true);
315 connect(_reconnectTimer, &QTimer::timeout, this, [this]() {
316 if (!_transport) {
317 startNTRIP();
318 }
319 });
320 }
321
322 _reconnectTimer->start(backoffMs);
323}
324
325void NTRIPManager::_rtcmDataReceived(const QByteArray& data)
326{
327 _bytesReceived += static_cast<quint64>(data.size());
328 _messagesReceived++;
329
330 qCDebug(NTRIPManagerLog) << "NTRIP Forwarding RTCM to vehicle:" << data.size() << "bytes";
331
332 if (!_rtcmMavlink && qgcApp()) {
333 _rtcmMavlink = qgcApp()->findChild<RTCMMavlink*>();
334 }
335
336 if (_rtcmMavlink) {
337 _rtcmMavlink->RTCMDataUpdate(data);
338
339 if (_connectionStatus != ConnectionStatus::Connected) {
340 _setStatus(ConnectionStatus::Connected, tr("Connected"));
341 }
342 } else {
343 qCWarning(NTRIPManagerLog) << "RTCMMavlink not ready; dropping" << data.size() << "bytes";
344 }
345
346 if (_udpForwardEnabled && _udpSocket && _udpTargetPort > 0) {
347 const qint64 sent = _udpSocket->writeDatagram(data, _udpTargetAddress, _udpTargetPort);
348 if (sent < 0) {
349 qCWarning(NTRIPManagerLog) << "NTRIP UDP forward failed:" << _udpSocket->errorString();
350 }
351 }
352}
353
354QByteArray NTRIPManager::makeGGA(const QGeoCoordinate& coord, double altitude_msl)
355{
356 const QTime utc = QDateTime::currentDateTimeUtc().time();
357 const QString hhmmss = QString("%1%2%3")
358 .arg(utc.hour(), 2, 10, QChar('0'))
359 .arg(utc.minute(), 2, 10, QChar('0'))
360 .arg(utc.second(), 2, 10, QChar('0'));
361
362 auto dmm = [](double deg, bool lat) -> QString {
363 double a = qFabs(deg);
364 int d = int(a);
365 double m = (a - d) * 60.0;
366
367 int m10000 = int(m * 10000.0 + 0.5);
368 double m_rounded = m10000 / 10000.0;
369 if (m_rounded >= 60.0) {
370 m_rounded -= 60.0;
371 d += 1;
372 }
373
374 QString mm = QString::number(m_rounded, 'f', 4);
375 if (m_rounded < 10.0) {
376 mm.prepend("0");
377 }
378
379 if (lat) {
380 return QString("%1%2").arg(d, 2, 10, QChar('0')).arg(mm);
381 } else {
382 return QString("%1%2").arg(d, 3, 10, QChar('0')).arg(mm);
383 }
384 };
385
386 const bool latNorth = coord.latitude() >= 0.0;
387 const bool lonEast = coord.longitude() >= 0.0;
388
389 const QString latField = dmm(coord.latitude(), true);
390 const QString lonField = dmm(coord.longitude(), false);
391
392 const QString core = QString("GPGGA,%1,%2,%3,%4,%5,1,12,1.0,%6,M,0.0,M,,")
393 .arg(hhmmss)
394 .arg(latField)
395 .arg(latNorth ? "N" : "S")
396 .arg(lonField)
397 .arg(lonEast ? "E" : "W")
398 .arg(QString::number(altitude_msl, 'f', 1));
399
400 quint8 cksum = 0;
401 const QByteArray coreBytes = core.toUtf8();
402 for (char ch : coreBytes) {
403 cksum ^= static_cast<quint8>(ch);
404 }
405
406 const QString cks = QString("%1").arg(cksum, 2, 16, QChar('0')).toUpper();
407 const QByteArray sentence = QByteArray("$") + coreBytes + QByteArray("*") + cks.toUtf8();
408 return sentence;
409}
410
411QPair<QGeoCoordinate, QString> NTRIPManager::_getBestPosition() const
412{
413 MultiVehicleManager* mvm = MultiVehicleManager::instance();
414 if (mvm) {
415 if (Vehicle* veh = mvm->activeVehicle()) {
416 FactGroup* gps = veh->gpsFactGroup();
417 if (gps) {
418 Fact* latF = gps->getFact(QStringLiteral("lat"));
419 Fact* lonF = gps->getFact(QStringLiteral("lon"));
420
421 if (latF && lonF) {
422 const double lat = latF->rawValue().toDouble();
423 const double lon = lonF->rawValue().toDouble();
424
425 if (qIsFinite(lat) && qIsFinite(lon) &&
426 !(lat == 0.0 && lon == 0.0) &&
427 qAbs(lat) <= 90.0 && qAbs(lon) <= 180.0) {
428
429 return {QGeoCoordinate(lat, lon, veh->coordinate().altitude()),
430 QStringLiteral("GPS Raw")};
431 }
432 }
433 }
434
435 QGeoCoordinate coord = veh->coordinate();
436 if (coord.isValid() && !(coord.latitude() == 0.0 && coord.longitude() == 0.0)) {
437 return {coord, QStringLiteral("Vehicle EKF")};
438 }
439 }
440 }
441
442 QGCPositionManager* posMgr = QGCPositionManager::instance();
443 if (posMgr) {
444 QGeoCoordinate coord = posMgr->gcsPosition();
445 if (coord.isValid() && !(coord.latitude() == 0.0 && coord.longitude() == 0.0)) {
446 return {coord, QStringLiteral("GCS Position")};
447 }
448 }
449
450 return {QGeoCoordinate(), QString()};
451}
452
453void NTRIPManager::_sendGGA()
454{
455 if (!_transport) {
456 return;
457 }
458
459 const auto [coord, srcUsed] = _getBestPosition();
460
461 if (!coord.isValid()) {
462 qCDebug(NTRIPManagerLog) << "NTRIP: No valid position, skipping GGA";
463 return;
464 }
465
466 if (_ggaTimer && _ggaTimer->interval() != 5000) {
467 _ggaTimer->setInterval(5000);
468 qCDebug(NTRIPManagerLog) << "NTRIP: Position acquired, reducing GGA to 5s";
469 }
470
471 double alt_msl = coord.altitude();
472 if (!qIsFinite(alt_msl)) {
473 alt_msl = 0.0;
474 }
475
476 const QByteArray gga = makeGGA(coord, alt_msl);
477 _transport->sendNMEA(gga);
478
479 if (!srcUsed.isEmpty() && srcUsed != _ggaSource) {
480 _ggaSource = srcUsed;
481 emit ggaSourceChanged();
482 }
483}
484
485void NTRIPManager::_onSettingChanged()
486{
487 if (_startStopBusy) {
488 return;
489 }
490
491 NTRIPSettings* settings = SettingsManager::instance()->ntripSettings();
492 bool shouldRun = settings && settings->ntripServerConnectEnabled() &&
493 settings->ntripServerConnectEnabled()->rawValue().toBool();
494 bool isRunning = (_transport != nullptr);
495
496 if (shouldRun && isRunning && settings) {
497 NTRIPTransportConfig config = _configFromSettings();
498 const bool udpFwd = settings->ntripUdpForwardEnabled()->rawValue().toBool();
499 const QString udpAddr = settings->ntripUdpTargetAddress()->rawValue().toString();
500 const quint16 udpPort = static_cast<quint16>(settings->ntripUdpTargetPort()->rawValue().toUInt());
501
502 if (config != _runningConfig ||
503 udpFwd != _runningUdpForward || udpAddr != _runningUdpAddr || udpPort != _runningUdpPort) {
504 qCDebug(NTRIPManagerLog) << "NTRIP settings changed while running, restarting";
505 stopNTRIP();
506 startNTRIP();
507 return;
508 }
509 }
510
511 const bool reconnectPending = _reconnectTimer && _reconnectTimer->isActive();
512
513 if (shouldRun && !isRunning && !reconnectPending) {
514 startNTRIP();
515 } else if (!shouldRun) {
516 if (_reconnectTimer) {
517 _reconnectTimer->stop();
518 }
519 if (isRunning) {
520 stopNTRIP();
521 } else if (reconnectPending) {
522 _reconnectAttempts = 0;
523 _setStatus(ConnectionStatus::Disconnected, tr("Disconnected"));
524 }
525 }
526}
527
528QmlObjectListModel* NTRIPManager::mountpointModel() const
529{
530 if (_sourceTableModel) {
531 return _sourceTableModel->mountpoints();
532 }
533 return nullptr;
534}
535
536void NTRIPManager::fetchMountpoints()
537{
538 if (_mountpointFetchStatus == MountpointFetchStatus::FetchInProgress) {
539 return;
540 }
541
542 if (_sourceTableModel && _sourceTableModel->count() > 0 && _sourceTableFetchedAtMs > 0) {
543 const qint64 age = QDateTime::currentMSecsSinceEpoch() - _sourceTableFetchedAtMs;
544 if (age < kSourceTableCacheTtlMs) {
545 qCDebug(NTRIPManagerLog) << "Source table cache hit, age:" << age << "ms";
546 _mountpointFetchStatus = MountpointFetchStatus::FetchSuccess;
548 return;
549 }
550 }
551
552 NTRIPSettings* settings = SettingsManager::instance()->ntripSettings();
553 if (!settings) {
554 return;
555 }
556
557 const QString host = settings->ntripServerHostAddress()->rawValue().toString();
558 const int port = settings->ntripServerPort()->rawValue().toInt();
559 const QString user = settings->ntripUsername()->rawValue().toString();
560 const QString pass = settings->ntripPassword()->rawValue().toString();
561 const bool useTls = settings->ntripUseTls() ? settings->ntripUseTls()->rawValue().toBool() : false;
562
563 if (host.isEmpty()) {
564 _mountpointFetchError = tr("Host address is empty");
565 _mountpointFetchStatus = MountpointFetchStatus::FetchError;
568 return;
569 }
570
571 if (!_sourceTableModel) {
572 _sourceTableModel = new NTRIPSourceTableModel(this);
574 }
575
576 if (_sourceTableFetcher) {
577 _sourceTableFetcher->deleteLater();
578 _sourceTableFetcher = nullptr;
579 }
580
581 _mountpointFetchStatus = MountpointFetchStatus::FetchInProgress;
582 _mountpointFetchError.clear();
585
586 _sourceTableFetcher = new NTRIPSourceTableFetcher(host, port, user, pass, useTls, this);
587
588 connect(_sourceTableFetcher, &NTRIPSourceTableFetcher::sourceTableReceived, this, [this](const QString& table) {
589 _sourceTableModel->parseSourceTable(table);
590 _sourceTableFetchedAtMs = QDateTime::currentMSecsSinceEpoch();
591
592 MultiVehicleManager* mvm = MultiVehicleManager::instance();
593 if (mvm) {
594 if (Vehicle* veh = mvm->activeVehicle()) {
595 QGeoCoordinate coord = veh->coordinate();
596 if (coord.isValid()) {
597 _sourceTableModel->updateDistances(coord);
598 }
599 }
600 }
601
602 _mountpointFetchStatus = MountpointFetchStatus::FetchSuccess;
604 qCDebug(NTRIPManagerLog) << "NTRIP source table fetched:" << _sourceTableModel->count() << "mountpoints";
605 });
606
607 connect(_sourceTableFetcher, &NTRIPSourceTableFetcher::error, this, [this](const QString& errorMsg) {
608 _mountpointFetchError = errorMsg;
609 _mountpointFetchStatus = MountpointFetchStatus::FetchError;
612 qCWarning(NTRIPManagerLog) << "NTRIP source table fetch error:" << errorMsg;
613 });
614
615 connect(_sourceTableFetcher, &NTRIPSourceTableFetcher::finished, this, [this]() {
616 _sourceTableFetcher->deleteLater();
617 _sourceTableFetcher = nullptr;
618 });
619
620 _sourceTableFetcher->fetch();
621}
622
623void NTRIPManager::selectMountpoint(const QString& mountpoint)
624{
625 NTRIPSettings* settings = SettingsManager::instance()->ntripSettings();
626 if (settings && settings->ntripMountpoint()) {
627 settings->ntripMountpoint()->setRawValue(mountpoint);
628 qCDebug(NTRIPManagerLog) << "NTRIP mountpoint selected:" << mountpoint;
629 }
630}
Q_APPLICATION_STATIC(NTRIPManager, _ntripManagerInstance)
#define qgcApp()
#define qApp
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Used to group Facts together into an object hierarachy.
Definition FactGroup.h:15
A Fact is used to hold a single value within the system.
Definition Fact.h:16
void rawValueChanged(const QVariant &value)
void error(const QString &errorMsg)
void sendNMEA(const QByteArray &nmea)
void RTCMDataUpdate(const QByteArray &message)
void statusMessageChanged()
void messagesReceivedChanged()
void bytesReceivedChanged()
void mountpointFetchStatusChanged()
void casterStatusChanged(CasterStatus status)
void dataRateChanged()
void ggaSourceChanged()
void connectionStatusChanged()
void mountpointFetchErrorChanged()
void mountpointModelChanged()
Fact *ntripUsername READ ntripUsername CONSTANT Fact * ntripUsername()
Fact *ntripServerHostAddress READ ntripServerHostAddress CONSTANT Fact * ntripServerHostAddress()
Fact *ntripUseTls READ ntripUseTls CONSTANT Fact * ntripUseTls()
Fact *ntripServerConnectEnabled READ ntripServerConnectEnabled CONSTANT Fact * ntripServerConnectEnabled()
Fact *ntripServerPort READ ntripServerPort CONSTANT Fact * ntripServerPort()
Fact *ntripUdpTargetAddress READ ntripUdpTargetAddress CONSTANT Fact * ntripUdpTargetAddress()
Fact *ntripWhitelist READ ntripWhitelist CONSTANT Fact * ntripWhitelist()
Fact *ntripMountpoint READ ntripMountpoint CONSTANT Fact * ntripMountpoint()
Fact *ntripPassword READ ntripPassword CONSTANT Fact * ntripPassword()
Fact *ntripUdpForwardEnabled READ ntripUdpForwardEnabled CONSTANT Fact * ntripUdpForwardEnabled()
Fact *ntripUdpTargetPort READ ntripUdpTargetPort CONSTANT Fact * ntripUdpTargetPort()
void error(const QString &errorMsg)
void sourceTableReceived(const QString &table)