QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
NTRIPConnectionStats.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <utility>
5
6#include "MonotonicClock.h"
8#include "RuntimeScheduler.h"
9
10QGC_LOGGING_CATEGORY(NTRIPConnectionStatsLog, "GPS.NTRIPConnectionStats")
11
12NTRIPConnectionStats::NTRIPConnectionStats(std::chrono::milliseconds staleAfter, QObject* parent,
13 RuntimeScheduler* scheduler)
14 : QObject(parent)
15 , _staleAfter(staleAfter)
16 , _scheduler(RuntimeScheduler::orDefault(scheduler, this))
17 , _publishTask(_scheduler, this)
18 , _rateTracker([this]() { return _scheduler->nowUs(); })
19{}
20
21std::chrono::milliseconds NTRIPConnectionStats::_now() const
22{
23 return std::chrono::milliseconds(_scheduler->nowMs());
24}
25
27{
28 _startedAt = _now();
29 _publishTask.scheduleRepeating(std::chrono::seconds{1}, [this]() {
30 _rateTracker.refresh();
31 (void) _updateDataStale(_now());
32 emit statsChanged();
33 });
34}
35
37{
38 _publishTask.cancel();
39 _startedAt = std::chrono::milliseconds::zero();
40 _rateTracker.resetRate();
41 emit statsChanged();
42}
43
45{
46 const auto age = MonotonicClock::age(_lastReceivedAt, std::chrono::milliseconds(_scheduler->nowMs()));
47 return age ? std::chrono::duration<double>(*age).count() : -1.0;
48}
49
50bool NTRIPConnectionStats::_updateDataStale(std::chrono::milliseconds now)
51{
52 const auto since = _lastReceivedAt > std::chrono::milliseconds::zero() ? _lastReceivedAt : _startedAt;
53 const auto age = MonotonicClock::age(since, now);
54 const bool stale = age && *age >= _staleAfter;
55 return std::exchange(_dataStale, stale) != stale;
56}
57
58void NTRIPConnectionStats::recordMessage(int bytes, int messageId, qint64 receivedAtMs)
59{
60 if (bytes <= 0) {
61 return;
62 }
63 _rateTracker.recordBytes(bytes);
64 _messagesReceived++;
65 ++_messageCountsById[messageId];
66 const auto now = _now();
67 const std::chrono::milliseconds receivedAt(receivedAtMs);
68 if (receivedAt <= std::chrono::milliseconds::zero() || receivedAt > now) {
69 qCWarning(NTRIPConnectionStatsLog) << "Invalid RTCM receipt timestamp:" << receivedAtMs;
70 } else {
71 _lastReceivedAt = (std::max) (_lastReceivedAt, receivedAt);
72 }
73 // A stream that recovers shows it at once; the counters follow at the next second.
74 if (_updateDataStale(now)) {
75 emit statsChanged();
76 }
77}
78
80{
81 _rateTracker.reset();
82 _messagesReceived = 0;
83 _lastReceivedAt = std::chrono::milliseconds::zero();
84 _startedAt = std::chrono::milliseconds::zero();
85 _messageCountsById.clear();
86 _dataStale = false;
87 emit statsChanged();
88}
89
90QList<RTCMMessageCount> NTRIPConnectionStats::messageCountsById() const
91{
92 QList<RTCMMessageCount> counts;
93 counts.reserve(_messageCountsById.size());
94 for (auto it = _messageCountsById.cbegin(); it != _messageCountsById.cend(); ++it) {
95 counts.append({it.key(), it.value()});
96 }
97 std::ranges::sort(counts, {}, &RTCMMessageCount::messageId);
98 return counts;
99}
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void reset()
Reset all counters and restart the rate window.
void recordBytes(qsizetype bytes)
Record incoming/outgoing bytes. Call whenever data passes through.
void refresh()
Advance the rate window without changing cumulative bytes.
void resetRate()
Zero the rate and restart its window; cumulative bytes are kept.
void recordMessage(int bytes, int messageId, qint64 receivedAtMs)
Count every message; health uses the newest valid receipt on the scheduler's clock.
QList< RTCMMessageCount > messageCountsById() const
qint64 nowMs() const
bool scheduleRepeating(std::chrono::microseconds interval, 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.