QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MAVLinkMessageField.cc
Go to the documentation of this file.
3#include "MAVLinkMessage.h"
4#include "QGCApplication.h"
6
7#include <QtGraphs/QLineSeries>
8#include <QtGraphs/QAbstractSeries>
9
10#include <algorithm>
11#include <cmath>
12
13static constexpr qreal kMinDelta = 1e-6;
14
15QGC_LOGGING_CATEGORY(MAVLinkMessageFieldLog, "AnalyzeView.MAVLinkMessageField")
16
17QGCMAVLinkMessageField::QGCMAVLinkMessageField(const QString &name, const QString &type, QGCMAVLinkMessage *parent)
18 : QObject(parent)
19 , _type(type)
20 , _name(name)
21 , _msg(parent)
22{
23 // qCDebug(MAVLinkMessageFieldLog) << Q_FUNC_INFO << this;
24
25 qCDebug(MAVLinkMessageFieldLog) << "Field:" << name << type;
26}
27
29{
30 // qCDebug(MAVLinkMessageFieldLog) << Q_FUNC_INFO << this;
31}
32
33void QGCMAVLinkMessageField::addSeries(MAVLinkChartController *chartController, QAbstractSeries *series)
34{
35 if (_pSeries) {
36 return;
37 }
38
39 _chartController = chartController;
40 _pSeries = series;
41 emit seriesChanged();
42
43 _dataIndex = 0;
44 _bucketCount = std::max(1, chartController->plotPixelWidth());
45 _bucketWidthMs = chartController->rangeXMs() / _bucketCount;
46 _currentBucketStart = -1;
48}
49
51{
52 if (!_pSeries) {
53 return;
54 }
55
56 _values.clear();
57 _rangeMin = std::numeric_limits<qreal>::max();
58 _rangeMax = std::numeric_limits<qreal>::lowest();
59 QLineSeries *const lineSeries = static_cast<QLineSeries*>(_pSeries);
60 lineSeries->replace(_values);
61 _pSeries = nullptr;
62 _chartController = nullptr;
63 _bucketCount = 0;
64 _bucketWidthMs = 0;
65 _currentBucketStart = -1;
66 _dataIndex = 0;
67 emit seriesChanged();
69}
70
71void QGCMAVLinkMessageField::resetBucketing(int bucketCount, qreal bucketWidthMs)
72{
73 _bucketCount = std::max(1, bucketCount);
74 _bucketWidthMs = bucketWidthMs;
75 _currentBucketStart = -1;
76 _currentBucketMin = 0;
77 _currentBucketMax = 0;
78 _dataIndex = 0;
79 _values.clear();
80 _rangeMin = std::numeric_limits<qreal>::max();
81 _rangeMax = std::numeric_limits<qreal>::lowest();
82}
83
85{
86 return (_msg->name() + ": " + _name);
87}
88
90{
91 if (_selectable != sel) {
92 _selectable = sel;
93 emit selectableChanged();
94 }
95}
96
98{
99 if (_chartController) {
100 return _chartController->chartIndex();
101 }
102
103 return 0;
104}
105
106void QGCMAVLinkMessageField::updateValue(const QString &newValue, qreal v)
107{
108 if (_value != newValue) {
109 _value = newValue;
110 emit valueChanged();
111 }
112
113 if (!_pSeries || !_chartController || _bucketCount <= 0) {
114 return;
115 }
116
117 const qreal now = qgcApp()->msecsSinceBoot();
118
119 if (_currentBucketStart < 0) {
120 // First sample — start first bucket
121 _currentBucketStart = now;
122 _currentBucketMin = v;
123 _currentBucketMax = v;
124 if (_chartController->rangeYIndex() == 0) {
125 bool changed = false;
126 if (std::abs(_rangeMin - v) > kMinDelta) {
127 _rangeMin = v;
128 changed = true;
129 }
130 if (std::abs(_rangeMax - v) > kMinDelta) {
131 _rangeMax = v;
132 changed = true;
133 }
134 if (changed) {
135 _chartController->updateYRange();
136 }
137 }
138 return;
139 }
140
141 if (now < _currentBucketStart + _bucketWidthMs) {
142 // Still in current bucket — update min/max
143 _currentBucketMin = std::min(_currentBucketMin, v);
144 _currentBucketMax = std::max(_currentBucketMax, v);
145 if (_chartController->rangeYIndex() == 0) {
146 bool changed = false;
147 if (v < _rangeMin - kMinDelta) {
148 _rangeMin = v;
149 changed = true;
150 }
151 if (v > _rangeMax + kMinDelta) {
152 _rangeMax = v;
153 changed = true;
154 }
155 if (changed) {
156 _chartController->updateYRange();
157 }
158 }
159 return;
160 }
161
162 // Crossed into next bucket — commit current bucket
163 _commitBucket();
164
165 // Start new bucket with this sample
166 _currentBucketStart = now;
167 _currentBucketMin = v;
168 _currentBucketMax = v;
169
170 // Update Y auto-range from committed data and current bucket
171 if (_chartController->rangeYIndex() != 0) {
172 return;
173 }
174
175 qreal vmin = _currentBucketMin;
176 qreal vmax = _currentBucketMax;
177 for (const QPointF &point : _values) {
178 vmin = std::min(vmin, point.y());
179 vmax = std::max(vmax, point.y());
180 }
181
182 bool changed = false;
183 if (std::abs(_rangeMin - vmin) > kMinDelta) {
184 _rangeMin = vmin;
185 changed = true;
186 }
187
188 if (std::abs(_rangeMax - vmax) > kMinDelta) {
189 _rangeMax = vmax;
190 changed = true;
191 }
192
193 if (changed) {
194 _chartController->updateYRange();
195 }
196}
197
198void QGCMAVLinkMessageField::_commitBucket()
199{
200 const qreal bucketMidTime = _currentBucketStart + _bucketWidthMs * 0.5;
201 const int maxPoints = _bucketCount * 2;
202
203 // Append min and max points for this bucket (always two points to keep capacity predictable)
204 const int count = _values.count();
205 if (count < maxPoints) {
206 _values.append(QPointF(bucketMidTime, _currentBucketMin));
207 _values.append(QPointF(bucketMidTime, _currentBucketMax));
208 } else {
209 // Ring buffer wrap
210 if (_dataIndex >= maxPoints) {
211 _dataIndex = 0;
212 }
213 _values[_dataIndex] = QPointF(bucketMidTime, _currentBucketMin);
214 _dataIndex++;
215 if (_dataIndex >= maxPoints) {
216 _dataIndex = 0;
217 }
218 _values[_dataIndex] = QPointF(bucketMidTime, _currentBucketMax);
219 _dataIndex++;
220 }
221}
222
224{
225 const int count = _values.count();
226
227 QList<QPointF> s;
228 s.reserve(count + 2);
229 int idx = _dataIndex;
230 for (int i = 0; i < count; i++, idx++) {
231 if (idx >= count) {
232 idx = 0;
233 }
234
235 const QPointF p(_values[idx]);
236 s.append(p);
237 }
238
239 // Append the in-progress bucket so the chart always shows the latest data
240 if (_currentBucketStart >= 0) {
241 const qreal now = qgcApp()->msecsSinceBoot();
242 s.append(QPointF(now, _currentBucketMin));
243 if (std::abs(_currentBucketMax - _currentBucketMin) > kMinDelta) {
244 s.append(QPointF(now, _currentBucketMax));
245 }
246 }
247
248 QLineSeries *const lineSeries = static_cast<QLineSeries*>(_pSeries);
249 if (s.isEmpty()) {
250 lineSeries->clear();
251 return;
252 }
253
254 lineSeries->replace(s);
255}
static constexpr qreal kMinDelta
static constexpr qreal kMinDelta
#define qgcApp()
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void updateValue(const QString &newValue, qreal v)
void addSeries(MAVLinkChartController *chartController, QAbstractSeries *series)
const QAbstractSeries * series() const
void resetBucketing(int bucketCount, qreal bucketWidthMs)
QString name() const