QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GPSReceiverDescriptor.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <array>
5
6#include <QtCore/QHash>
7
8using namespace Qt::StringLiterals;
9
10namespace {
13
14constexpr std::array DESCRIPTORS{
16 .type = GPSType::trimble,
17 .name = "Trimble"_L1,
18 .manufacturerId = 1,
19 .capabilities = {.recognized = true, .rtkBase = true, .surveyIn = true},
20 .surveyDuration = Duration::ElapsedTime,
21 .configurableSurveyDuration = true,
22 },
24 .type = GPSType::septentrio,
25 .name = "Septentrio"_L1,
26 .manufacturerId = 2,
27 .protocol = "SBF"_L1,
28 .capabilities = {.recognized = true, .rtkBase = true, .surveyIn = true},
29 .surveyDuration = Duration::ElapsedTime,
30 },
32 .type = GPSType::femto,
33 .name = "Femtomes"_L1,
34 .manufacturerId = 3,
35 .capabilities = {.recognized = true, .rtkBase = true, .surveyIn = true},
36 .surveyDuration = Duration::ElapsedTime,
37 // POSAVE ON uses the receiver's averaging policy; the driver does not program a duration.
38 .configurableSurveyDuration = false,
39 },
41 .type = GPSType::ublox,
42 .name = "u-blox"_L1,
43 .manufacturerId = 4,
44 .protocol = "UBX"_L1,
45 .capabilities = {.recognized = true, .rtkBase = true, .surveyIn = true, .compactObservations = true},
46 .surveyAccuracy = Accuracy::PositionAccuracy,
47 .surveyDuration = Duration::ElapsedTime,
48 .configurableSurveyDuration = true,
49 .fixedBaseAccuracy = true,
50 },
52 .type = GPSType::unicore,
53 .name = "Unicore"_L1,
54 .manufacturerId = 5,
55 .capabilities = {.recognized = true, .rtkBase = true, .receiverAveraging = true},
56 },
58 .type = GPSType::quectel,
59 .name = "Quectel"_L1,
60 .manufacturerId = 6,
61 .capabilities = {.recognized = true, .rtkBase = true, .surveyIn = true, .persistentConfiguration = true},
62 .surveyAccuracy = Accuracy::ObservationFilter,
63 .surveyDuration = Duration::AcceptedObservations,
64 .configurableSurveyDuration = true,
65 .restartOnConnect = true,
66 .surveyMaySavePosition = true,
67 },
69 .type = GPSType::passive,
70 .name = "passive NMEA"_L1,
71 .manufacturerId = 7,
72 .protocol = "NMEA"_L1,
73 .capabilities = {.recognized = true, .passive = true},
74 },
75};
76
78consteval bool registryComplete()
79{
80 for (int type = 0; type < static_cast<int>(GPSType::automatic); ++type) {
81 if (std::ranges::count(DESCRIPTORS, static_cast<GPSType>(type), &GPSReceiverDescriptor::type) != 1) {
82 return false;
83 }
84 }
85 for (const auto& descriptor : DESCRIPTORS) {
86 if (descriptor.type == GPSType::automatic || descriptor.manufacturerId == GPS_AUTOMATIC_MANUFACTURER ||
87 std::ranges::count(DESCRIPTORS, descriptor.manufacturerId, &GPSReceiverDescriptor::manufacturerId) != 1) {
88 return false;
89 }
90 }
91 return DESCRIPTORS.size() == static_cast<size_t>(GPSType::automatic);
92}
93
94static_assert(registryComplete(), "Each GPSType needs one descriptor with a unique, nonzero manufacturer ID");
95} // namespace
96
97std::span<const GPSReceiverDescriptor> gpsReceiverDescriptors()
98{
99 return DESCRIPTORS;
100}
101
103{
104 const auto entry = std::ranges::find(DESCRIPTORS, type, &GPSReceiverDescriptor::type);
105 return entry == DESCRIPTORS.end() ? nullptr : &*entry;
106}
107
109{
110 const auto* descriptor = gpsReceiverDescriptor(type);
111 return descriptor ? QString(descriptor->name) : QString();
112}
113
115{
116 const auto* descriptor = gpsReceiverDescriptor(type);
117 if (!descriptor || descriptor->protocol.isEmpty()) {
118 return {};
119 }
120 const QString protocol(descriptor->protocol);
121 return type == GPSType::passive ? protocol : QStringLiteral("%1 (%2)").arg(gpsReceiverName(type), protocol);
122}
123
125{
126 const auto entry = std::ranges::find(DESCRIPTORS, manufacturer, &GPSReceiverDescriptor::manufacturerId);
127 return entry == DESCRIPTORS.end() ? nullptr : &*entry;
128}
129
130std::optional<GPSType> gpsReceiverTypeForManufacturer(int manufacturer)
131{
132 if (manufacturer == GPS_AUTOMATIC_MANUFACTURER) {
133 return GPSType::automatic;
134 }
135 if (const auto* descriptor = gpsReceiverDescriptorForManufacturer(manufacturer)) {
136 return descriptor->type;
137 }
138 return std::nullopt;
139}
140
142{
143 const auto* descriptor = gpsReceiverDescriptor(type);
144 return descriptor ? descriptor->manufacturerId : GPS_AUTOMATIC_MANUFACTURER;
145}
146
148{
149 if (type == GPSType::automatic) {
150 // Detection finds a base family; until then every option some base family supports is accepted.
152 for (const auto& descriptor : DESCRIPTORS) {
153 const auto& capabilities = descriptor.capabilities;
154 if (!capabilities.rtkBase) {
155 continue;
156 }
157 combined.recognized = true;
158 combined.rtkBase = true;
159 combined.surveyIn |= capabilities.surveyIn;
160 combined.receiverAveraging |= capabilities.receiverAveraging;
161 combined.persistentConfiguration |= capabilities.persistentConfiguration;
162 combined.compactObservations |= capabilities.compactObservations;
163 }
164 return combined;
165 }
166
167 const auto* descriptor = gpsReceiverDescriptor(type);
168 if (!descriptor) {
169 return {};
170 }
171 return descriptor->capabilities;
172}
173
174namespace {
175GPSReceiverPresentation buildPresentation(int manufacturer)
176{
177 const auto* selected = gpsReceiverDescriptorForManufacturer(manufacturer);
178 GPSReceiverPresentation presentation;
179 for (const auto& descriptor : DESCRIPTORS) {
180 if (manufacturer != GPS_AUTOMATIC_MANUFACTURER && &descriptor != selected) {
181 continue;
182 }
183 presentation.rtkBase |= descriptor.capabilities.rtkBase;
184 presentation.surveyIn |= descriptor.capabilities.surveyIn;
185 presentation.receiverAveraging |= descriptor.capabilities.receiverAveraging;
186 presentation.compactObservations |= descriptor.capabilities.compactObservations;
187 presentation.surveyAccuracy |= descriptor.surveyAccuracy != Accuracy::Unavailable;
188 presentation.surveyDuration |= descriptor.configurableSurveyDuration;
189 presentation.fixedBaseAccuracy |= descriptor.fixedBaseAccuracy;
190 // Automatic offers these for whichever family it identifies: the consent a connection may pass and the notes
191 // on that family's side effects.
192 presentation.persistentConfiguration |= descriptor.capabilities.persistentConfiguration;
193 presentation.restartOnConnect |= descriptor.restartOnConnect;
194 presentation.surveyMaySavePosition |= descriptor.surveyMaySavePosition;
195 }
196 // Labels and live status describe one family, which Automatic only knows once connected.
197 presentation.specificReceiver = selected != nullptr;
198 presentation.automatic = manufacturer == GPS_AUTOMATIC_MANUFACTURER;
199 presentation.passive = selected && selected->capabilities.passive;
200 presentation.observationAccuracyFilter = selected && selected->surveyAccuracy == Accuracy::ObservationFilter;
201 presentation.acceptedObservationTime = selected && selected->surveyDuration == Duration::AcceptedObservations;
202 presentation.reportsSurveyDuration = selected && selected->surveyDuration != Duration::Unavailable;
203 return presentation;
204}
205} // namespace
206
208{
209 // Immutable after first use, so QML bindings see one value per receiver family.
210 static const QHash<int, GPSReceiverPresentation> presentations = [] {
211 QHash<int, GPSReceiverPresentation> result{
213 for (const auto& descriptor : DESCRIPTORS) {
214 result.insert(descriptor.manufacturerId, buildPresentation(descriptor.manufacturerId));
215 }
216 return result;
217 }();
218 static const GPSReceiverPresentation unknown = buildPresentation(-1);
219 const auto presentation = presentations.constFind(manufacturer);
220 return presentation != presentations.cend() ? *presentation : unknown;
221}
const GPSReceiverDescriptor * gpsReceiverDescriptorForManufacturer(int manufacturer)
std::span< const GPSReceiverDescriptor > gpsReceiverDescriptors()
QString gpsInputProtocolName(GPSType type)
int gpsReceiverManufacturerForType(GPSType type)
The persisted manufacturer of type, or GPS_AUTOMATIC_MANUFACTURER when it has none.
const GPSReceiverDescriptor * gpsReceiverDescriptor(GPSType type)
GPSReceiverCapabilities gpsReceiverCapabilities(GPSType type)
What type supports, for either role; unknown receiver families support nothing.
QString gpsReceiverName(GPSType type)
The name of type's receiver family as shown to users; empty when no descriptor exists.
std::optional< GPSType > gpsReceiverTypeForManufacturer(int manufacturer)
The family a persisted manufacturer selects, GPSType::automatic for GPS_AUTOMATIC_MANUFACTURER; empty...
const GPSReceiverPresentation & gpsReceiverPresentation(int manufacturer)
constexpr int GPS_AUTOMATIC_MANUFACTURER
Software request support, not a guarantee for every physical receiver model or firmware.
Persisted manufacturer IDs are distinct from GPSType values.
bool automatic
The family is detected when connecting.