QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
NTRIPSourceTable.cc
Go to the documentation of this file.
1#include "NTRIPSourceTable.h"
2
3#include <algorithm>
4#include <utility>
5
6#include <QtCore/QCoreApplication>
7#include <QtCore/QStringList>
8#include <QtCore/qnumeric.h>
9
10bool ntripSourceTableComplete(QByteArrayView body, qsizetype checkedSize)
11{
12 static constexpr QByteArrayView terminator("ENDSOURCETABLE");
13 // A terminator the earlier check missed ends past its data, so it starts no earlier than this.
14 const qsizetype from = (std::max) (qsizetype(0), checkedSize - terminator.size());
15 for (qsizetype offset = body.indexOf(terminator, from); offset >= 0;
16 offset = body.indexOf(terminator, offset + 1)) {
17 const qsizetype end = offset + terminator.size();
18 if ((offset == 0 || body.at(offset - 1) == '\n') &&
19 (end == body.size() || body.at(end) == '\r' || body.at(end) == '\n')) {
20 return true;
21 }
22 }
23 return false;
24}
25
26std::optional<NTRIPMountpoint> NTRIPMountpoint::fromSourceTableLine(const QString& line)
27{
28 // Need >= 18 fields; split keeps empty fields, so a trailing ';' is harmless
29 // (the extra empty field parses as 0 / "" for its column).
30 const QStringList fields = line.split(';');
31 if (fields.size() < 18 || fields.at(0).trimmed().toUpper() != QStringLiteral("STR")) {
32 return std::nullopt;
33 }
34
36 mp.mountpoint = fields.at(1).trimmed();
37 mp.format = fields.at(3).trimmed();
38 mp.navSystem = fields.at(6).trimmed();
39 mp.country = fields.at(8).trimmed();
40 const auto parseCoord = [](const QString& s, double limit) -> double {
41 bool ok = false;
42 const double v = s.trimmed().toDouble(&ok);
43 return (ok && qIsFinite(v) && qAbs(v) <= limit) ? v : qQNaN();
44 };
45 mp.latitude = parseCoord(fields.at(9), 90.0);
46 mp.longitude = parseCoord(fields.at(10), 180.0);
47 mp.bitrate = fields.at(17).trimmed().toInt();
48 return mp;
49}
50
51void NTRIPMountpoint::updateDistance(const QGeoCoordinate& from)
52{
53 const QGeoCoordinate mountCoord(latitude, longitude);
54 if (!from.isValid() || !mountCoord.isValid() || (latitude == 0.0 && longitude == 0.0)) {
55 distanceKm = -1.0;
56 return;
57 }
58 distanceKm = from.distanceTo(mountCoord) / 1000.0;
59}
60
62{
63 QStringList parts;
64 for (const QString& part : {format, navSystem, country}) {
65 if (!part.isEmpty()) {
66 parts.append(part);
67 }
68 }
69 if (bitrate > 0) {
70 //: Mountpoint bitrate in bits per second
71 parts.append(QCoreApplication::translate("NTRIPMountpoint", "%1 bps").arg(bitrate));
72 }
73 if (distanceKm >= 0) {
74 //: Distance to the mountpoint in kilometers
75 parts.append(QCoreApplication::translate("NTRIPMountpoint", "%1 km").arg(distanceKm, 0, 'f', 1));
76 }
77 //: Separator between mountpoint details
78 return parts.join(QCoreApplication::translate("NTRIPMountpoint", " ยท "));
79}
80
81// ---------------------------------------------------------------------------
82// NTRIPSourceTableModel
83// ---------------------------------------------------------------------------
84
85// A const range keeps the model read-only; the base only records its address and reads rows after construction.
87 : QRangeModel(&std::as_const(_mountpoints), parent)
88{}
89
90void NTRIPSourceTableModel::parseSourceTable(const QString& raw, const QGeoCoordinate& from)
91{
92 QList<NTRIPMountpoint> mountpoints;
93 const QStringList lines = raw.split('\n');
94 for (const QString& line : lines) {
95 if (auto mp = NTRIPMountpoint::fromSourceTableLine(line.trimmed())) {
96 mp->updateDistance(from);
97 mountpoints.append(*mp);
98 }
99 }
100 beginResetModel();
101 _mountpoints = std::move(mountpoints);
102 endResetModel();
103 emit countChanged();
104}
105
106void NTRIPSourceTableModel::updateDistances(const QGeoCoordinate& from)
107{
108 qsizetype firstChanged = -1;
109 qsizetype lastChanged = -1;
110 for (qsizetype row = 0; row < _mountpoints.size(); ++row) {
111 NTRIPMountpoint& mountpoint = _mountpoints[row];
112 const double previous = mountpoint.distanceKm;
113 mountpoint.updateDistance(from);
114 if (mountpoint.distanceKm != previous) {
115 firstChanged = firstChanged < 0 ? row : firstChanged;
116 lastChanged = row;
117 }
118 }
119 if (firstChanged >= 0) {
120 emit dataChanged(index(static_cast<int>(firstChanged), 0), index(static_cast<int>(lastChanged), 0),
122 }
123}
124
126{
127 if (_mountpoints.isEmpty()) {
128 return;
129 }
130 beginResetModel();
131 _mountpoints.clear();
132 endResetModel();
133 emit countChanged();
134}
135
136// ---------------------------------------------------------------------------
137// NTRIPSourceTableSortModel
138// ---------------------------------------------------------------------------
139
141 : QSortFilterProxyModel(parent)
142{
143 setSourceModel(source);
145 sort(0);
146 // Nothing is filtered, and the source announces its count after the proxy has processed the reset.
148}
149
150bool NTRIPSourceTableSortModel::lessThan(const QModelIndex& left, const QModelIndex& right) const
151{
152 const double leftKm = left.data(sortRole()).toDouble();
153 const double rightKm = right.data(sortRole()).toDouble();
154 if ((leftKm < 0) != (rightKm < 0)) {
155 return rightKm < 0;
156 }
157 if (leftKm >= 0 && leftKm != rightKm) {
158 return leftKm < rightKm;
159 }
160 // A strict order keeps equal distances in caster order, also when a distance update reinserts rows.
161 return left.row() < right.row();
162}
bool ntripSourceTableComplete(QByteArrayView body, qsizetype checkedSize)
void updateDistances(const QGeoCoordinate &from)
Recompute distances; one dataChanged for DistanceKmRole spans the rows whose distance changed.
void parseSourceTable(const QString &raw, const QGeoCoordinate &from={})
Publish the parsed rows and their distances in a single reset.
NTRIPSourceTableModel(QObject *parent=nullptr)
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
NTRIPSourceTableSortModel(NTRIPSourceTableModel *source, QObject *parent=nullptr)
QString details() const
void updateDistance(const QGeoCoordinate &from)
Recompute distanceKm, or mark it unknown for invalid reference/mountpoint coordinates.
static std::optional< NTRIPMountpoint > fromSourceTableLine(const QString &line)
Parses one source-table line ("STR;..."); none unless it is a valid STR row.
QString mountpoint
The format, navigation systems, country, bitrate and distance that are known, as one line.