QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
MockLinkPX4Calibration.cc
Go to the documentation of this file.
2#include "MAVLinkLib.h"
3#include "MockLink.h"
4
5#include <algorithm>
6
7// Side names in PX4 detect_orientation_return order (see PX4 detect_orientation_str())
8static constexpr const char *kSideNames[MockLinkPX4Calibration::kSideCount] = {
9 "back", // tail down
10 "front", // nose down
11 "left",
12 "right",
13 "up", // upside down
14 "down", // right side up
15};
16
17// Ideal accel readings per side, same order as kSideNames (see PX4 accel_calibration_worker())
19 "[9.810 0.000 0.000]",
20 "[-9.810 0.000 0.000]",
21 "[0.000 9.810 0.000]",
22 "[0.000 -9.810 0.000]",
23 "[0.000 0.000 9.810]",
24 "[0.000 0.000 -9.810]",
25};
26
27// Device ids stored in CAL_MAG0_ID/CAL_ACC0_ID after a successful calibration,
28// matching the values in PX4MockLink.params
29static constexpr int32_t kMagDeviceId = 197388;
30static constexpr int32_t kAccelDeviceId = 1310988;
31
33 : _mockLink(mockLink)
34{
35}
36
38{
39 _startCalibration(CalType::Mag);
40}
41
43{
44 _startCalibration(CalType::Accel);
45}
46
47void MockLinkPX4Calibration::_startCalibration(CalType calType)
48{
49 QMutexLocker locker(&_mutex);
50
51 _calType = calType;
52 _requestedPose = -1;
53 _activeSide = -1;
54 _sideTickCount = 0;
55 _finishTickCount = -1;
56 std::fill(std::begin(_sideDone), std::end(_sideDone), false);
57
58 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] calibration started: 2 %1")
59 .arg(QLatin1String((calType == CalType::Mag) ? "mag" : "accel")));
60}
61
63{
64 QMutexLocker locker(&_mutex);
65
66 if (_calType == CalType::None) {
67 return false;
68 }
69
70 _calType = CalType::None;
71 _requestedPose = -1;
72 _activeSide = -1;
73 _finishTickCount = -1;
74
75 _mockLink->sendStatusTextMessage(MAV_SEVERITY_CRITICAL, QStringLiteral("[cal] calibration cancelled"));
76 return true;
77}
78
80{
81 QMutexLocker locker(&_mutex);
82 return _calType != CalType::None;
83}
84
86{
87 QMutexLocker locker(&_mutex);
88 _requestedPose = static_cast<int>(pose);
89}
90
92{
93 QMutexLocker locker(&_mutex);
94
95 if (_calType == CalType::None) {
96 return;
97 }
98
99 if (_finishTickCount >= 0) {
100 // Finishing phase: simulates the firmware calculating the final calibration
101 // values before reporting completion. Gives the UI time to display the last
102 // side as completed while the calibration is still active.
103 _finishTickCount++;
104 if (_finishTickCount >= kTicksToFinish) {
105 const CalType calType = _calType;
106 _calType = CalType::None;
107 _finishTickCount = -1;
108 if (calType == CalType::Mag) {
109 // Store the calibration result like the firmware does, before reporting
110 // done so the subsequent param refresh by QGC picks it up
111 _mockLink->setInt32ParamValue(MAV_COMP_ID_AUTOPILOT1, QStringLiteral("CAL_MAG0_ID"), kMagDeviceId);
112 // The mag fit calculation reports its own final progress
113 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] progress <100>"));
114 } else {
115 _mockLink->setInt32ParamValue(MAV_COMP_ID_AUTOPILOT1, QStringLiteral("CAL_ACC0_ID"), kAccelDeviceId);
116 }
117 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] calibration done: %1")
118 .arg(QLatin1String((calType == CalType::Mag) ? "mag" : "accel")));
119 }
120 return;
121 }
122
123 if (_activeSide >= 0) {
124 _sideTick();
125 } else {
126 _detectPose();
127 }
128}
129
130int MockLinkPX4Calibration::_doneCount() const
131{
132 int count = 0;
133 for (bool done : _sideDone) {
134 if (done) {
135 count++;
136 }
137 }
138 return count;
139}
140
142void MockLinkPX4Calibration::_detectPose()
143{
144 const int pose = _requestedPose;
145 if ((pose < 0) || (pose >= kSideCount)) {
146 return;
147 }
148 _requestedPose = -1; // consume
149
150 if (_sideDone[pose]) {
151 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] %1 side already completed").arg(QLatin1String(kSideNames[pose])));
152 return;
153 }
154
155 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] %1 orientation detected").arg(QLatin1String(kSideNames[pose])));
156 if (_calType == CalType::Accel) {
157 // The accel worker starts sampling immediately (see PX4 accel_calibration_worker())
158 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] Hold still, measuring %1 side").arg(QLatin1String(kSideNames[pose])));
159 }
160 _activeSide = pose;
161 _sideTickCount = 0;
162}
163
166void MockLinkPX4Calibration::_sideTick()
167{
168 _sideTickCount++;
169
170 switch (_calType) {
171 case CalType::Mag:
172 _magSideTick();
173 break;
174 case CalType::Accel:
175 _accelSideTick();
176 break;
177 case CalType::None:
178 return;
179 }
180
181 if (_doneCount() == kSideCount) {
182 // All sides collected: enter the finishing phase
183 _finishTickCount = 0;
184 }
185}
186
188void MockLinkPX4Calibration::_magSideTick()
189{
190 if (_sideTickCount < kTicksPerSide) {
191 // Same progress calculation as PX4 mag_calibration_worker()
192 const unsigned progress = (_doneCount() * 100 / kSideCount) + ((100 / kSideCount) * _sideTickCount / kTicksPerSide);
193 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] %1 side calibration: progress <%2>").arg(QLatin1String(kSideNames[_activeSide])).arg(progress));
194 return;
195 }
196
197 _sideDone[_activeSide] = true;
198 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] %1 side done, rotate to a different side").arg(QLatin1String(kSideNames[_activeSide])));
199 _activeSide = -1;
200}
201
204void MockLinkPX4Calibration::_accelSideTick()
205{
206 if (_sideTickCount < kTicksPerSide) {
207 return;
208 }
209
210 _sideDone[_activeSide] = true;
211 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] %1 side result: %2").arg(QLatin1String(kSideNames[_activeSide]), QLatin1String(kSideAccelResults[_activeSide])));
212 // Same progress calculation as PX4 accel_calibration_worker(): 17 * doneCount.
213 // Intentionally reaches 102 after the final side, matching firmware; the UI
214 // progress bar clamps it to 100%.
215 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] progress <%1>").arg(17 * _doneCount()));
216 _mockLink->sendStatusTextMessage(MAV_SEVERITY_INFO, QStringLiteral("[cal] %1 side done, rotate to a different side").arg(QLatin1String(kSideNames[_activeSide])));
217 _activeSide = -1;
218}
static constexpr const char * kSideNames[MockLinkPX4Calibration::kSideCount]
static constexpr const char * kSideAccelResults[MockLinkPX4Calibration::kSideCount]
static constexpr int32_t kAccelDeviceId
static constexpr int32_t kMagDeviceId
static constexpr int kSideCount
MockLinkPX4Calibration(MockLink *mockLink)
Pose
Vehicle poses. Order matches the PX4 detect_orientation_return enum.
void run10HzTasks()
Called by MockLink::run10HzTasks on the worker thread.