10#include <QtCore/QDeadlineTimer>
11#include <QtCore/QMetaEnum>
29 const auto& match = signatures.
match();
30 if (!match || match->family->type == configured) {
33 QString evidence = match->evidence;
34 evidence[0] = evidence[0].toUpper();
35 return QStringLiteral(
"%1 were received; this looks like a %2 receiver")
46 std::unique_ptr<GPSProtocolRuntime>
runtime;
49 std::vector<GPSConfigurationEvidence>
evidence;
67 , _transport(transport)
69 , _sinks(std::move(sinks))
70 , _clock(std::move(clock))
71 , _state(std::make_unique<
State>())
78 return _state->evidence;
83 return _state->configurationError;
88 return _state->consentRequired;
93 return _state->runtime ? _state->runtime->identity() : QString();
98 _state = std::make_unique<State>();
100 _state->configurationError =
error;
101 qCWarning(GPSDriverLog) <<
error;
104 if (_type != GPSType::passive) {
105 const auto& mode = _config.
base.
mode;
106 if (std::holds_alternative<GPSBaseStationConfig::Fixed>(mode)) {
107 qCDebug(GPSDriverLog) <<
"Fixed base configured";
108 }
else if (
const auto* averaging = std::get_if<GPSBaseStationConfig::ReceiverAveraging>(&mode)) {
109 qCDebug(GPSDriverLog) <<
"Receiver-managed averaging maximum duration (s):"
110 << averaging->maximumDuration.count();
111 }
else if (
const auto* survey = std::get_if<GPSBaseStationConfig::SurveyIn>(&mode)) {
112 qCDebug(GPSDriverLog) <<
"Survey-in accuracy (m):" << survey->accuracyMeters
113 <<
"minimum duration (s):" << survey->duration.count();
120 const auto result = _transport.
read(bytes, deadline.remaining(_clock.
nowUs()));
122 static_cast<size_t>(result.bytesRead) <= bytes.size();
123 _state->cycle.activity |= data;
124 if (data && _state->signatures && _state->configuring) {
125 _state->signatures->push(bytes.first(
static_cast<size_t>(result.bytesRead)));
131 const auto remaining = deadline.remaining(_clock.
nowUs());
132 if (remaining == std::chrono::milliseconds::zero()) {
136 const QDeadlineTimer transportDeadline = deadline.untilUs == UINT64_MAX
137 ? QDeadlineTimer(QDeadlineTimer::Forever, Qt::PreciseTimer)
138 : QDeadlineTimer(remaining, Qt::PreciseTimer);
139 return _transport.
write(bytes, transportDeadline);
150 if (_state->configuring) {
151 _state->evidence.push_back(result);
158 _state->configurationError = QStringLiteral(
"Selected baud rate differs from the transport's fixed baud rate");
159 qCWarning(GPSDriverLog) << _state->configurationError;
162 if (_type == GPSType::automatic) {
163 return _configureDetected(std::move(io), std::move(observer), baudrate);
167 _state->configurationError =
168 QStringLiteral(
"Unsupported GPS type: %1")
169 .arg(QLatin1StringView(QMetaEnum::fromType<GPSType>().valueToKey(
static_cast<int>(_type))));
170 qCWarning(GPSDriverLog) <<
"Unsupported GPS type:" << _type;
177 if (_type != GPSType::passive) {
180 return _configureFamily(*family, std::move(io), std::move(observer),
187 _state->configuring =
true;
190 _state->configuring =
false;
191 if (!detection.
found()) {
194 ? QStringLiteral(
"Receiver detection cancelled")
195 : QStringLiteral(
"No supported receiver was identified");
196 qCWarning(GPSDriverLog) <<
"Receiver detection failed:" << _state->configurationError;
204 bool compactFallback =
false;
206 if (compactFallback) {
207 qCWarning(GPSDriverLog).noquote()
208 << QStringLiteral(
"Detected %1 receiver has no compact (MSM4) RTCM option; it sends its standard set")
212 _state->configurationError =
error;
213 qCWarning(GPSDriverLog).noquote() <<
error;
218 return _configureFamily(
219 *detection.
family, std::move(io), std::move(observer),
220 {.base = config.base, .allowPersistentChanges = config.allowPersistentChanges, .detectedBaud = detection.baud},
221 baudrate, QStringLiteral(
"Detected %1 receiver at %2 baud").arg(name).arg(detection.
baud));
227 _state->runtime = std::make_unique<GPSProtocolRuntime>(family, std::move(io), std::move(observer));
228 _state->configuring =
true;
229 const bool configured = _state->runtime->configure(
config, baudrate);
230 _state->configuring =
false;
231 const auto signatures = std::exchange(_state->signatures,
nullptr);
233 QString
error = _state->runtime->errorDetail();
234 if (
error.isEmpty()) {
235 error = QStringLiteral(
"Receiver configuration failed");
237 if (!detected.isEmpty()) {
238 error = QStringLiteral(
"%1: %2").arg(detected,
error);
239 }
else if (
const QString hint = signatures ? mismatchHint(*signatures, family.
type) : QString();
241 error += (
error.endsWith(u
'.') ? QStringLiteral(
" ") : QStringLiteral(
". ")) + hint;
243 _state->configurationError =
error;
245 qCWarning(GPSDriverLog) <<
"Driver configuration failed for type" << family.
type <<
error;
246 _state->runtime.reset();
249 _state->configurationError.clear();
255 if (!_state->runtime) {
259 _publishExpiredSatellites();
260 const GPSReceiveUpdates decoded = _state->runtime->receive(timeout);
261 _publishExpiredSatellites();
263 return {.error = _state->runtime->error(),
264 .updates = _state->cycle.updates,
265 .detail = _state->runtime->errorDetail()};
272 const GPSReadResult failure = _transport.
read({}, std::chrono::milliseconds::zero());
278 .updates = _state->cycle.updates};
283 _state->cycle.activity |= !batch.
events.empty();
284 for (
const auto& event : batch.
events) {
285 std::visit([
this](
const auto& report) { _handle(report); }, event);
291 _state->integrity = report;
296 if (_state->configuring) {
299 _state->cycle.usefulData =
true;
308 if (!_state->configuring) {
309 _handleSatellites(_state->satellites.update(report, _clock.
nowUs()));
315 if (!_state->configuring) {
316 _handleSatellites(_state->satellites.update(report, _clock.
nowUs()));
322 _state->cycle.usefulData =
true;
324 _publishSatellites(report);
329 _state->cycle.usefulData =
true;
337 _state->cycle.usefulData =
true;
338 if (!_state->configuring && _sinks.
onRTCM) {
346 _state->satellites = {};
352void GPSDriver::_publishExpiredSatellites()
354 if (
const auto expired = _state->satellites.expire(_clock.
nowUs())) {
356 _publishSatellites(*expired);
363 if (_state->lastSatellites == report) {
366 _state->lastSatellites = report;
@ InvalidArgument
The driver requested an invalid write, or received before configuring.
@ Cancelled
A stop was requested; not a receiver or link fault.
@ ConsentRequired
Configuration needs a persistent receiver change the caller did not allow.
@ Transport
The link could not read, write, or change its baud rate.
@ Activity
Diagnostics or partial input, never proof of navigation liveness.
@ Data
Useful reports, even without a registered sink.
GPSReceiverConfig gpsReceiverConfigForDetected(GPSType detected, GPSReceiverConfig config, bool *compactFallback)
QString gpsDetectedReceiverConfigError(GPSType detected, const GPSReceiverConfig &config)
QString gpsReceiverConfigError(GPSType type, const GPSReceiverConfig &config)
An empty diagnostic means the receiver supports the role and all supplied settings.
QString gpsReceiverName(GPSType type)
The name of type's receiver family as shown to users; empty when no descriptor exists.
std::span< const GPSReceiverFamily *const > gpsReceiverFamilies()
Every native receiver family, in lookup order.
const GPSReceiverFamily * gpsReceiverFamily(GPSType type)
The family for type, or nullptr when no native family supports it.
#define QGC_LOGGING_CATEGORY(name, categoryStr)
QString receiverIdentity() const
Receiver model and firmware reported by the configured protocol; empty when unknown.
const QString & configurationError() const
Diagnostic from the latest configure() failure; cleared when a new attempt starts.
bool configurationNeedsConsent() const
Whether the latest configure() failed because the receiver needs persistent changes the request did n...
GPSReceiveResult receiveOutcome(std::chrono::milliseconds timeout)
bool configure()
Create and configure the underlying driver.
GPSDriver(GPSType type, GPSTransport &transport, const GPSReceiverConfig &config, GPSDriverSinks sinks, GPSClock clock={})
Deadlines, report times and satellite freshness run on clock; the default is the steady clock.
const std::vector< GPSConfigurationEvidence > & configurationEvidence() const
Latest configure() attempt; remains available after failure. Caller-thread access only.
GPSReceiverDetection detect(unsigned baud=0)
Identifies the receiver at baud, which then every family is probed at, or else at each baudCandidates...
const std::optional< Match > & match() const
The first family signature since the last reset.
virtual bool fatalError() const =0
GPSWriteResult write(QByteArrayView bytes, QDeadlineTimer deadline)
virtual bool setBaudrate(unsigned baudrate)
virtual GPSReadResult read(std::span< uint8_t > buffer, std::chrono::milliseconds timeout)=0
virtual unsigned fixedBaudrate() const
Nonzero when the link cannot follow baud-rate changes (for example, a serial bridge).
const GPSCancelToken & cancelToken() const
GPSPositionReport position(const GPSDecodedPosition &source, const GPSIntegrityReport &diagnostic, uint64_t nowUs)
The monotonic clock receiver sessions run on: the steady clock unless tests inject a virtual one.
std::function< uint64_t()> nowUs
The receiver configuration requested for one configure() attempt.
Evidence from one configuration command, not a claim of physical receiver operation.
Decoded navigation plus whether the producer accepted its velocity solution.
std::function< void(const GPSSatelliteReport &)> onSatelliteInfo
std::function< void(const GPSPositionReport &)> onPosition
std::function< void(const GPSSurveyReport &)> onSurveyIn
std::function< void(const QByteArray &)> onRTCM
One complete frame; copying it shares the receiver's buffer.
std::function< void(GPSType)> onReceiverDetected
GPSReceiveUpdates updates
GPSDecodedData::SatelliteCounts satellites
GPSIntegrityReport integrity
QString configurationError
std::vector< GPSConfigurationEvidence > evidence
std::optional< GPSSatelliteReport > lastSatellites
std::unique_ptr< GPSReceiverSignatures > signatures
Other families' signatures in the replies to a configured type, while it configures.
std::unique_ptr< GPSProtocolRuntime > runtime
Events decoded from one span of received bytes, in publication order.
std::vector< GPSProtocolEvent > events
One RTCM3 frame the receiver produced, including its header and CRC.
bool allowPersistentChanges
Per-connection consent to persistent receiver settings and the required restart.
GPSBaseStationConfig base
uint32_t baudRate
Zero lets a configurable receiver detect the rate; passive serial input requires an explicit rate.
The receiver detection found, or why it found none.
const GPSReceiverFamily * family
Null when no family was identified.
GPSProtocolError error
Protocol when nothing was identified; Cancelled or Transport when the link ended detection.
unsigned baud
The rate the receiver answered at.
unsigned autoBaudRate
Rate passed to configure() when none is selected; zero requests detection.
std::function< GPSReadResult(std::span< uint8_t >, GPSDeadline)> read
GPSCancelToken cancelToken
Checked before each write and baud change; reads observe it in the transport.
std::function< GPSWriteResult(std::span< const uint8_t >, GPSDeadline)> write
GPSClock clock
Without a wait, the runtime sleeps in short slices until the duration elapses or cancelToken is cance...
std::function< GPSBaudStatus(unsigned)> setBaudrate
Observers of runtime output, invoked synchronously on the runtime thread.
std::function< void(const GPSConfigurationEvidence &)> commandFinished
Every completed command, during configuration and streaming alike.
std::function< void(const GPSEventBatch &)> decoded
Borrowed for the callback; it may decode nested input through GPSProtocolRuntime::consume().