QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
GPSSourceHealth.cc
Go to the documentation of this file.
1#include "GPSSourceHealth.h"
2
3#include <algorithm>
4#include <chrono>
5#include <utility>
6
7#include "MonotonicClock.h"
9#include "RuntimeScheduler.h"
10
11QGC_LOGGING_CATEGORY(GPSSourceHealthLog, "GPS.Core.GPSSourceHealth")
12
14 : QObject(parent)
15 , _scheduler(RuntimeScheduler::orDefault(scheduler, this))
16 , _positionTask(_scheduler, this)
17{
18 qCDebug(GPSSourceHealthLog) << this;
19}
20
22{
23 qCDebug(GPSSourceHealthLog) << this;
24}
25
26std::optional<std::chrono::milliseconds> GPSSourceHealth::_age(quint64 timestampUs) const
27{
28 const auto age = MonotonicClock::age(timestampUs, _scheduler->nowUs());
29 if (!age) {
30 return std::nullopt;
31 }
32 return std::chrono::duration_cast<std::chrono::milliseconds>(*age);
33}
34
35std::optional<GPSObservation> GPSSourceHealth::acceptedObservation(
36 GPSObservation::PositionUse use, std::optional<std::chrono::milliseconds> maximumAge) const
37{
38 if (_position.invalidated ||
39 _remaining(_position.observation.monotonicTimestampUs, maximumAge) == std::chrono::microseconds::zero()) {
40 return std::nullopt;
41 }
42 return _position.observation.projected(use);
43}
44
45std::chrono::microseconds GPSSourceHealth::_remaining(quint64 timestampUs,
46 std::optional<std::chrono::milliseconds> maximumAge) const
47{
48 const auto lifetime = maximumAge ? (std::min) (_freshnessTimeout, *maximumAge) : _freshnessTimeout;
49 return lifetime > std::chrono::milliseconds::zero()
50 ? MonotonicClock::remaining(timestampUs, _scheduler->nowUs(), lifetime)
51 : std::chrono::microseconds::zero();
52}
53
55{
56 const State previousState = state();
57 ++_observationRevision;
58 _positionTask.cancel();
59 _position.observation = observation;
60 // Temporal rejection lasts until the next observation.
61 _position.invalidated = !_age(observation.monotonicTimestampUs);
62 _position.state = _updatedPositionState();
63 _schedulePositionExpiry();
64 _logStateChange(previousState);
65 emit positionChanged();
66}
67
68void GPSSourceHealth::_schedulePositionExpiry()
69{
70 _positionTask.cancel();
71 const auto remaining = _remaining(_position.observation.monotonicTimestampUs);
72 if (remaining == std::chrono::microseconds::zero()) {
73 return;
74 }
75 _positionTask.schedule(remaining, [this]() { _setState(State::Stale); });
76}
77
78void GPSSourceHealth::_logStateChange(State previous) const
79{
80 if (previous != state()) {
81 qCDebug(GPSSourceHealthLog) << this << "Position health changed:" << previous << "->" << state();
82 }
83}
84
85void GPSSourceHealth::_setState(State state)
86{
87 if (_position.state != state) {
88 const State previous = _position.state;
89 _position.state = state;
90 _logStateChange(previous);
91 emit positionChanged();
92 }
93}
94
96{
97 const bool wasInvalidated = std::exchange(_position.invalidated, true);
98 _schedulePositionExpiry();
99 if (state() != State::Invalid) {
100 const auto age = _age(_position.observation.monotonicTimestampUs);
101 _setState(age && *age >= _freshnessTimeout ? State::Stale : State::Invalid);
102 } else if (!wasInvalidated) {
103 // State is unchanged, but acceptedObservation() no longer offers the retained fix.
104 emit positionChanged();
105 }
106}
107
109{
110 const State previousState = state();
111 _positionTask.cancel();
112 _position = {};
113 _logStateChange(previousState);
114 emit positionChanged();
115}
116
117void GPSSourceHealth::setFreshnessTimeout(std::chrono::milliseconds timeout)
118{
119 _freshnessTimeout = (std::max) (std::chrono::milliseconds{1}, timeout);
120 if (state() == State::NoData) {
121 return;
122 }
123 const State previousState = state();
124 _position.state = _updatedPositionState();
125 _schedulePositionExpiry();
126 _logStateChange(previousState);
127 emit positionChanged();
128}
129
130GPSSourceHealth::State GPSSourceHealth::_updatedPositionState() const
131{
132 const auto age = _age(_position.observation.monotonicTimestampUs);
133 if (!age) {
134 return State::Invalid;
135 }
136 if (*age >= _freshnessTimeout) {
137 return State::Stale;
138 }
139 return _position.invalidated ? state() : (_position.observation.usable() ? State::Usable : State::Invalid);
140}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
Session health is independent of transport readiness and RTK survey-in validity.
void setFreshnessTimeout(std::chrono::milliseconds timeout)
void positionChanged()
void updateObservation(const GPSObservation &observation)
std::optional< GPSObservation > acceptedObservation(GPSObservation::PositionUse use=GPSObservation::PositionUse::GroundStation, std::optional< std::chrono::milliseconds > maximumAge=std::nullopt) const
~GPSSourceHealth() override
State state() const
virtual quint64 nowUs() const =0
bool schedule(std::chrono::microseconds delay, RuntimeScheduler::Callback callback)
constexpr std::optional< std::chrono::duration< Rep, Period > > age(std::chrono::duration< Rep, Period > timestamp, std::chrono::duration< Rep, Period > now)
Age of timestamp at now, both in the same unit; empty for a missing or future timestamp.
constexpr std::chrono::microseconds remaining(uint64_t timestampUs, uint64_t nowUs, std::chrono::microseconds lifetime)
Time left before a receipt reaches lifetime; zero for a missing, future or expired one.
Receiver-independent data. Unknown metadata remains absent, never a manufactured zero.
quint64 monotonicTimestampUs