QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
NTRIPSourceTable.cc
Go to the documentation of this file.
1#include "NTRIPSourceTable.h"
3#include "QGCNetworkHelper.h"
5
6#include <algorithm>
7#include <QtNetwork/QNetworkAccessManager>
8
9QGC_LOGGING_CATEGORY(NTRIPSourceTableLog, "GPS.NTRIPSourceTable")
10
11NTRIPMountpointModel* NTRIPMountpointModel::fromSourceTableLine(const QString& line, QObject* parent)
12{
13 const QStringList fields = line.split(';');
14 if (fields.size() < 18 || fields.at(0).trimmed().toUpper() != QStringLiteral("STR")) {
15 return nullptr;
16 }
17
18 auto* model = new NTRIPMountpointModel(parent);
19 model->_mountpoint = fields.at(1).trimmed();
20 model->_identifier = fields.at(2).trimmed();
21 model->_format = fields.at(3).trimmed();
22 model->_formatDetails = fields.at(4).trimmed();
23 model->_carrier = fields.at(5).trimmed().toInt();
24 model->_navSystem = fields.at(6).trimmed();
25 model->_network = fields.at(7).trimmed();
26 model->_country = fields.at(8).trimmed();
27 model->_latitude = fields.at(9).trimmed().toDouble();
28 model->_longitude = fields.at(10).trimmed().toDouble();
29 model->_nmea = fields.at(11).trimmed() == QStringLiteral("1");
30 model->_solution = fields.at(12).trimmed() == QStringLiteral("1");
31 model->_generator = fields.at(13).trimmed();
32 model->_compression = fields.at(14).trimmed();
33 model->_authentication = fields.at(15).trimmed();
34 model->_fee = fields.at(16).trimmed() == QStringLiteral("Y");
35 model->_bitrate = fields.at(17).trimmed().toInt();
36
37 return model;
38}
39
40void NTRIPMountpointModel::updateDistance(const QGeoCoordinate& from)
41{
42 if (!from.isValid() || (_latitude == 0.0 && _longitude == 0.0)) {
43 return;
44 }
45
46 const QGeoCoordinate mountCoord(_latitude, _longitude);
47 const double dist = from.distanceTo(mountCoord) / 1000.0;
48 if (qFuzzyCompare(_distanceKm, dist)) {
49 return;
50 }
51 _distanceKm = dist;
52 emit distanceKmChanged();
53}
54
55// ---------------------------------------------------------------------------
56// NTRIPSourceTableModel
57// ---------------------------------------------------------------------------
58
59NTRIPSourceTableModel::NTRIPSourceTableModel(QObject* parent)
60 : QObject(parent)
61 , _mountpoints(new QmlObjectListModel(this))
62{
63}
64
65int NTRIPSourceTableModel::count() const
66{
67 return _mountpoints ? _mountpoints->count() : 0;
68}
69
70void NTRIPSourceTableModel::parseSourceTable(const QString& raw)
71{
72 clear();
73
74 const QStringList lines = raw.split('\n');
75 for (const QString& line : lines) {
76 const QString trimmed = line.trimmed();
77 if (trimmed.isEmpty() || trimmed.startsWith(QStringLiteral("ENDSOURCETABLE"))) {
78 continue;
79 }
80 NTRIPMountpointModel* mp = NTRIPMountpointModel::fromSourceTableLine(trimmed, _mountpoints);
81 if (mp) {
82 _mountpoints->append(mp);
83 }
84 }
85
86 emit countChanged();
87}
88
89void NTRIPSourceTableModel::updateDistances(const QGeoCoordinate& from)
90{
91 if (!_mountpoints) {
92 return;
93 }
94 for (int i = 0; i < _mountpoints->count(); ++i) {
95 auto* mp = qobject_cast<NTRIPMountpointModel*>(_mountpoints->get(i));
96 if (mp) {
97 mp->updateDistance(from);
98 }
99 }
100 sortByDistance();
101}
102
103void NTRIPSourceTableModel::sortByDistance()
104{
105 if (!_mountpoints || _mountpoints->count() < 2) {
106 return;
107 }
108
109 QObjectList sorted = *_mountpoints->objectList();
110 std::sort(sorted.begin(), sorted.end(), [](QObject* a, QObject* b) {
111 auto* ma = qobject_cast<NTRIPMountpointModel*>(a);
112 auto* mb = qobject_cast<NTRIPMountpointModel*>(b);
113 if (!ma || !mb) return false;
114 const double da = ma->distanceKm();
115 const double db = mb->distanceKm();
116 if (da < 0 && db < 0) return false;
117 if (da < 0) return false;
118 if (db < 0) return true;
119 return da < db;
120 });
121 _mountpoints->swapObjectList(sorted);
122}
123
124void NTRIPSourceTableModel::clear()
125{
126 if (_mountpoints) {
127 _mountpoints->clearAndDeleteContents();
128 emit countChanged();
129 }
130}
131
132// ---------------------------------------------------------------------------
133// NTRIPSourceTableFetcher
134// ---------------------------------------------------------------------------
135
137 const QString& username, const QString& password,
138 bool useTls,
139 QObject* parent)
140 : QObject(parent)
141 , _host(host)
142 , _port(port)
143 , _username(username)
144 , _password(password)
145 , _useTls(useTls)
146{
147 _networkManager = QGCNetworkHelper::createNetworkManager(this);
148}
149
151{
152 QUrl url;
153 url.setScheme(_useTls ? QStringLiteral("https") : QStringLiteral("http"));
154 url.setHost(_host);
155 url.setPort(_port);
156 url.setPath(QStringLiteral("/"));
157
159 config.timeoutMs = kFetchTimeoutMs;
160 config.userAgent = QStringLiteral("QGC-NTRIP");
161 config.http2Allowed = false;
162 config.cacheEnabled = false;
163
164 QNetworkRequest request = QGCNetworkHelper::createRequest(url, config);
165 request.setRawHeader("Ntrip-Version", "Ntrip/2.0");
166 if (!_username.isEmpty() || !_password.isEmpty()) {
167 QGCNetworkHelper::setBasicAuth(request, _username, _password);
168 }
169
170 _reply = _networkManager->get(request);
171 connect(_reply, &QNetworkReply::finished, this, &NTRIPSourceTableFetcher::_onReplyFinished);
172}
173
174void NTRIPSourceTableFetcher::_onReplyFinished()
175{
176 if (!_reply) {
177 emit error(tr("No reply received"));
178 emit finished();
179 return;
180 }
181
182 const QString body = QString::fromUtf8(_reply->readAll());
183
184 if (_reply->error() != QNetworkReply::NoError && !body.contains(QStringLiteral("ENDSOURCETABLE"))) {
186 _reply->deleteLater();
187 _reply = nullptr;
188 emit finished();
189 return;
190 }
191 _reply->deleteLater();
192 _reply = nullptr;
193
194 if (!body.contains(QStringLiteral("ENDSOURCETABLE"))) {
195 emit error(tr("Response does not contain a valid source table"));
196 emit finished();
197 return;
198 }
199
200 emit sourceTableReceived(body);
201 emit finished();
202}
Error error
#define QGC_LOGGING_CATEGORY(name, categoryStr)
static constexpr int kFetchTimeoutMs
void sourceTableReceived(const QString &table)
NTRIPSourceTableFetcher(const QString &host, int port, const QString &username, const QString &password, bool useTls=false, QObject *parent=nullptr)
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
QObject * get(int index)
int count() const override final
void clearAndDeleteContents() override final
Clears the list and calls deleteLater on each entry.
QObjectList swapObjectList(const QObjectList &newlist)
QList< QObject * > * objectList()
QNetworkAccessManager * createNetworkManager(QObject *parent)
QNetworkRequest createRequest(const QUrl &url, const RequestConfig &config)
void setBasicAuth(QNetworkRequest &request, const QString &credentials)
QString errorMessage(const QNetworkReply *reply)
Common request configuration options.