22 qCDebug(GimbalControllerLog) <<
this;
27 _rateSenderTimer.setInterval(500);
28 (void) connect(&_rateSenderTimer, &QTimer::timeout,
this, &GimbalController::_rateSenderTimeout);
33 qCDebug(GimbalControllerLog) <<
this;
36void GimbalController::_initialConnectCompleted()
38 _initialConnectComplete =
true;
44 qCCritical(GimbalControllerLog) <<
"Set active gimbal: attempted to set a nullptr, returning";
48 if (gimbal != _activeGimbal) {
49 qCDebug(GimbalControllerLog) <<
"Set active gimbal:" << gimbal;
50 _activeGimbal = gimbal;
57 if (!_initialConnectComplete) {
61 switch (message.msgid) {
62 case MAVLINK_MSG_ID_HEARTBEAT:
63 _handleHeartbeat(message);
65 case MAVLINK_MSG_ID_GIMBAL_MANAGER_INFORMATION:
66 _handleGimbalManagerInformation(message);
68 case MAVLINK_MSG_ID_GIMBAL_MANAGER_STATUS:
69 _handleGimbalManagerStatus(message);
71 case MAVLINK_MSG_ID_GIMBAL_DEVICE_ATTITUDE_STATUS:
72 _handleGimbalDeviceAttitudeStatus(message);
81 if (!_potentialGimbalManagers.contains(message.compid)) {
82 qCDebug(GimbalControllerLog) <<
"new potential gimbal manager component:" << message.compid;
85 PotentialGimbalManager &gimbalManager = _potentialGimbalManagers[message.compid];
90 if (!gimbalManager.receivedGimbalManagerInformation && (gimbalManager.requestGimbalManagerInformationRetries > 0)) {
91 _requestGimbalInformation(message.compid);
92 --gimbalManager.requestGimbalManagerInformationRetries;
96void GimbalController::_handleGimbalManagerInformation(
const mavlink_message_t &message)
98 mavlink_gimbal_manager_information_t information{};
99 mavlink_msg_gimbal_manager_information_decode(&message, &information);
101 if (information.gimbal_device_id == 0) {
102 qCWarning(GimbalControllerLog) <<
"_handleGimbalManagerInformation for invalid gimbal device:"
103 << information.gimbal_device_id <<
", from component id:" << message.compid;
107 qCDebug(GimbalControllerLog) <<
"_handleGimbalManagerInformation for gimbal device:" << information.gimbal_device_id <<
", component id:" << message.compid;
109 const GimbalPairId pairId{message.compid, information.gimbal_device_id};
111 auto gimbalIt = _potentialGimbals.find(pairId);
112 if (gimbalIt == _potentialGimbals.constEnd()) {
113 gimbalIt = _potentialGimbals.insert(pairId,
new Gimbal(
this));
116 Gimbal *
const gimbal = gimbalIt.value();
121 if (!gimbal->_receivedGimbalManagerInformation) {
122 qCDebug(GimbalControllerLog) <<
"gimbal manager with compId:" << message.compid
123 <<
" is responsible for gimbal device:" << information.gimbal_device_id;
126 gimbal->_receivedGimbalManagerInformation =
true;
128 PotentialGimbalManager &gimbalManager = _potentialGimbalManagers[message.compid];
129 gimbalManager.receivedGimbalManagerInformation =
true;
131 _checkComplete(*gimbal, pairId);
134void GimbalController::_handleGimbalManagerStatus(
const mavlink_message_t &message)
136 mavlink_gimbal_manager_status_t status{};
137 mavlink_msg_gimbal_manager_status_decode(&message, &status);
141 if (status.gimbal_device_id == 0) {
142 qCDebug(GimbalControllerLog) <<
"gimbal manager with compId:" << message.compid
143 <<
"reported status of gimbal device id:" << status.gimbal_device_id <<
"which is not a valid gimbal device id";
147 const GimbalPairId pairId{message.compid, status.gimbal_device_id};
149 auto gimbalIt = _potentialGimbals.find(pairId);
150 if (gimbalIt == _potentialGimbals.constEnd()) {
151 gimbalIt = _potentialGimbals.insert(pairId,
new Gimbal(
this));
154 Gimbal *
const gimbal = gimbalIt.value();
157 }
else if (gimbal->
deviceId()->
rawValue().toUInt() != status.gimbal_device_id) {
158 qCWarning(GimbalControllerLog) <<
"conflicting GIMBAL_MANAGER_STATUS.gimbal_device_id:" << status.gimbal_device_id;
164 qCWarning(GimbalControllerLog) <<
"conflicting GIMBAL_MANAGER_STATUS compid:" << message.compid;
168 if (!gimbal->_receivedGimbalManagerStatus) {
169 qCDebug(GimbalControllerLog) <<
"_handleGimbalManagerStatus: gimbal manager with compId" << message.compid
170 <<
"is responsible for gimbal device" << status.gimbal_device_id;
173 gimbal->_receivedGimbalManagerStatus =
true;
175 const bool haveControl =
179 const bool othersHaveControl = !haveControl &&
180 (status.primary_control_sysid != 0 && status.primary_control_compid != 0);
190 _checkComplete(*gimbal, pairId);
193void GimbalController::_handleGimbalDeviceAttitudeStatus(
const mavlink_message_t &message)
195 mavlink_gimbal_device_attitude_status_t attitude_status{};
196 mavlink_msg_gimbal_device_attitude_status_decode(&message, &attitude_status);
198 GimbalPairId pairId{};
200 if (attitude_status.gimbal_device_id == 0) {
202 pairId.deviceId = message.compid;
205 const auto foundGimbal = std::find_if(_potentialGimbals.begin(), _potentialGimbals.end(),
206 [pairId](
Gimbal *gimbal) { return (gimbal->deviceId()->rawValue().toUInt() == pairId.deviceId); });
208 if (foundGimbal == _potentialGimbals.constEnd()) {
209 qCDebug(GimbalControllerLog) <<
"_handleGimbalDeviceAttitudeStatus for unknown device id:"
210 << pairId.deviceId <<
"from component id:" << message.compid;
214 pairId.managerCompid = foundGimbal.key().managerCompid;
215 }
else if (attitude_status.gimbal_device_id <= 6) {
217 pairId.deviceId = attitude_status.gimbal_device_id;
218 pairId.managerCompid = message.compid;
221 qCDebug(GimbalControllerLog) <<
"_handleGimbalDeviceAttitudeStatus for invalid device id: "
222 << attitude_status.gimbal_device_id <<
" from component id: " << message.compid;
226 auto gimbalIt = _potentialGimbals.find(pairId);
227 if (gimbalIt == _potentialGimbals.end()) {
228 gimbalIt = _potentialGimbals.insert(pairId,
new Gimbal(
this));
231 Gimbal *
const gimbal = gimbalIt.value();
233 gimbal->
setRetracted((attitude_status.flags & GIMBAL_DEVICE_FLAGS_RETRACT) > 0);
234 gimbal->
setYawLock((attitude_status.flags & GIMBAL_DEVICE_FLAGS_YAW_LOCK) > 0);
235 gimbal->_neutral = (attitude_status.flags & GIMBAL_DEVICE_FLAGS_NEUTRAL) > 0;
237 float roll, pitch, yaw;
238 mavlink_quaternion_to_euler(attitude_status.q, &roll, &pitch, &yaw);
243 const bool yaw_in_vehicle_frame = _yawInVehicleFrame(attitude_status.flags);
244 if (yaw_in_vehicle_frame) {
245 const float bodyYaw = qRadiansToDegrees(yaw);
246 float absoluteYaw = bodyYaw + _vehicle->
heading()->
rawValue().toFloat();
247 if (absoluteYaw > 180.0f) {
248 absoluteYaw -= 360.0f;
255 const float absoluteYaw = qRadiansToDegrees(yaw);
256 float bodyYaw = absoluteYaw - _vehicle->
heading()->
rawValue().toFloat();
257 if (bodyYaw < -180.0f) {
265 gimbal->_receivedGimbalDeviceAttitudeStatus =
true;
267 _checkComplete(*gimbal, pairId);
270void GimbalController::_requestGimbalInformation(uint8_t compid)
272 qCDebug(GimbalControllerLog) <<
"_requestGimbalInformation(" << compid <<
")";
276 MAV_CMD_REQUEST_MESSAGE,
278 MAVLINK_MSG_ID_GIMBAL_MANAGER_INFORMATION);
282void GimbalController::_checkComplete(
Gimbal &gimbal, GimbalPairId pairId)
284 if (gimbal._isComplete) {
289 if (!gimbal._receivedGimbalManagerInformation && gimbal._requestInformationRetries > 0) {
290 _requestGimbalInformation(pairId.managerCompid);
291 --gimbal._requestInformationRetries;
294 static qint64 lastRequestStatusMessage = 0;
295 qint64 now = QDateTime::currentMSecsSinceEpoch();
296 if (!gimbal._receivedGimbalManagerStatus && (gimbal._requestStatusRetries > 0) && (now - lastRequestStatusMessage > 1000)) {
297 lastRequestStatusMessage = now;
299 MAV_CMD_SET_MESSAGE_INTERVAL,
301 MAVLINK_MSG_ID_GIMBAL_MANAGER_STATUS,
302 (gimbal._requestStatusRetries > 2) ? 0 : 5000000);
303 --gimbal._requestStatusRetries;
304 qCDebug(GimbalControllerLog) <<
"attempt to set GIMBAL_MANAGER_STATUS message at"
305 << (gimbal._requestStatusRetries > 2 ?
"default rate" :
"0.2 Hz") <<
"interval for device:"
306 << gimbal.
deviceId()->
rawValue().toUInt() <<
"manager compID:" << pairId.managerCompid
307 <<
", retries remaining:" << gimbal._requestStatusRetries;
310 if (!gimbal._receivedGimbalDeviceAttitudeStatus && (gimbal._requestAttitudeRetries > 0) &&
311 gimbal._receivedGimbalManagerInformation && (pairId.deviceId != 0)) {
315 uint8_t gimbalDeviceCompid = pairId.deviceId;
317 if (gimbalDeviceCompid <= 6) {
318 gimbalDeviceCompid = pairId.managerCompid;
321 MAV_CMD_SET_MESSAGE_INTERVAL,
323 MAVLINK_MSG_ID_GIMBAL_DEVICE_ATTITUDE_STATUS,
326 --gimbal._requestAttitudeRetries;
329 if (!gimbal._receivedGimbalManagerInformation || !gimbal._receivedGimbalManagerStatus || !gimbal._receivedGimbalDeviceAttitudeStatus) {
334 gimbal._isComplete =
true;
337 if (!_activeGimbal) {
341 _gimbals->
append(&gimbal);
343 _vehicle->
_addFactGroup(&gimbal, QStringLiteral(
"%1%2%3").arg(_gimbalFactGroupNamePrefix).arg(pairId.managerCompid).arg(pairId.deviceId));
346bool GimbalController::_tryGetGimbalControl()
348 if (!_activeGimbal) {
349 qCCritical(GimbalControllerLog) <<
"_tryGetGimbalControl: active gimbal is nullptr, returning";
354 qCDebug(GimbalControllerLog) <<
"Others in control, showing popup for user to confirm control..";
358 qCDebug(GimbalControllerLog) <<
"Nobody in control, acquiring control ourselves..";
365bool GimbalController::_yawInVehicleFrame(uint32_t flags)
367 if ((flags & GIMBAL_DEVICE_FLAGS_YAW_IN_VEHICLE_FRAME) > 0) {
369 }
else if ((flags & GIMBAL_DEVICE_FLAGS_YAW_IN_EARTH_FRAME) > 0) {
373 return ((flags & GIMBAL_DEVICE_FLAGS_YAW_LOCK) == 0);
379 if (!_activeGimbal) {
380 qCCritical(GimbalControllerLog) <<
"gimbalPitchStart: active gimbal is nullptr, returning";
392 if (!_activeGimbal) {
393 qCCritical(GimbalControllerLog) <<
"gimbalYawStart: active gimbal is nullptr, returning";
404 if (!_activeGimbal) {
405 qCCritical(GimbalControllerLog) <<
"gimbalPitchStop: active gimbal is nullptr, returning";
415 if (!_activeGimbal) {
416 qCCritical(GimbalControllerLog) <<
"gimbalYawStop: active gimbal is nullptr, returning";
426 if (!_activeGimbal) {
427 qCCritical(GimbalControllerLog) <<
"gimbalYawStep: active gimbal is nullptr, returning";
437 if (!_activeGimbal) {
438 qCCritical(GimbalControllerLog) <<
"gimbalOnScreenControl: active gimbal is nullptr, returning";
446 const float panIncDesired = panPct * hFov * 0.5f;
447 const float tiltIncDesired = tiltPct * vFov * 0.5f;
449 const float panDesired = panIncDesired + _activeGimbal->
bodyYaw()->
rawValue().toFloat();
452 if (_activeGimbal->
yawLock()) {
457 }
else if (clickAndDrag) {
463 const float panIncDesired = panPct * maxSpeed * 0.1f;
464 const float tiltIncDesired = tiltPct * maxSpeed * 0.1f;
466 const float panDesired = panIncDesired + _activeGimbal->
bodyYaw()->
rawValue().toFloat();
469 if (_activeGimbal->
yawLock()) {
479 if (!_tryGetGimbalControl()) {
483 _rateSenderTimer.stop();
489 const unsigned flags = GIMBAL_MANAGER_FLAGS_ROLL_LOCK
490 | GIMBAL_MANAGER_FLAGS_PITCH_LOCK
491 | GIMBAL_MANAGER_FLAGS_YAW_IN_VEHICLE_FRAME;
495 MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW,
508 if (!_tryGetGimbalControl()) {
512 _rateSenderTimer.stop();
526 const unsigned flags = GIMBAL_MANAGER_FLAGS_ROLL_LOCK
527 | GIMBAL_MANAGER_FLAGS_PITCH_LOCK
528 | GIMBAL_MANAGER_FLAGS_YAW_LOCK
529 | GIMBAL_MANAGER_FLAGS_YAW_IN_EARTH_FRAME;
533 MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW,
546 if (!_tryGetGimbalControl()) {
552 flags |= GIMBAL_DEVICE_FLAGS_RETRACT;
554 flags &= ~GIMBAL_DEVICE_FLAGS_RETRACT;
562 if (!_tryGetGimbalControl()) {
569 _sendGimbalAttitudeRates(_activeGimbal->
pitchRate(), _activeGimbal->
yawRate());
572 if ((_activeGimbal->
pitchRate() == 0.f) && (_activeGimbal->
yawRate() == 0.f)) {
573 _rateSenderTimer.stop();
575 _rateSenderTimer.start();
581 if (!_tryGetGimbalControl()) {
585 _sendGimbalAttitudeRates(pitch_rate_deg_s, yaw_rate_deg_s);
587 if (pitch_rate_deg_s == 0.f && yaw_rate_deg_s == 0.f) {
588 _rateSenderTimer.stop();
590 _rateSenderTimer.start();
594void GimbalController::_sendGimbalAttitudeRates(
float pitch_rate_deg_s,
595 float yaw_rate_deg_s)
600 qCDebug(GimbalControllerLog) <<
"_sendGimbalAttitudeRates: primary link gone!";
605 GIMBAL_MANAGER_FLAGS_ROLL_LOCK |
606 GIMBAL_MANAGER_FLAGS_PITCH_LOCK |
607 GIMBAL_MANAGER_FLAGS_YAW_IN_VEHICLE_FRAME;
610 if (_activeGimbal->
yawLock()) {
611 flags |= GIMBAL_MANAGER_FLAGS_YAW_LOCK;
614 const float qnan[4] = {NAN, NAN, NAN, NAN};
617 mavlink_msg_gimbal_manager_set_attitude_pack_chan(
620 sharedLink->mavlinkChannel(),
628 qDegreesToRadians(pitch_rate_deg_s),
629 qDegreesToRadians(yaw_rate_deg_s)
635void GimbalController::_rateSenderTimeout()
643 if (!_tryGetGimbalControl()) {
648 uint32_t flags = GIMBAL_DEVICE_FLAGS_ROLL_LOCK | GIMBAL_DEVICE_FLAGS_PITCH_LOCK;
650 flags |= GIMBAL_DEVICE_FLAGS_YAW_LOCK;
658 const bool yaw_in_vehicle_frame = _yawInVehicleFrame(flags);
662 MAV_CMD_DO_GIMBAL_MANAGER_PITCHYAW,
675 if (!_activeGimbal) {
676 qCCritical(GimbalControllerLog) <<
"acquireGimbalControl: active gimbal is nullptr, returning";
682 MAV_CMD_DO_GIMBAL_MANAGER_CONFIGURE,
695 if (!_activeGimbal) {
696 qCCritical(GimbalControllerLog) <<
"releaseGimbalControl: active gimbal is nullptr, returning";
702 MAV_CMD_DO_GIMBAL_MANAGER_CONFIGURE,
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
void _addFactGroup(FactGroup *factGroup, const QString &name)
QVariant rawValue() const
Value after translation.
void sendPitchYawFlags(uint32_t flags)
Q_INVOKABLE void sendGimbalRate(float pitch_rate_deg_s, float yaw_rate_deg_s)
Q_INVOKABLE void setGimbalYawLock(bool set)
Q_INVOKABLE void centerGimbal()
Gimbal * activeGimbal() const
void activeGimbalChanged()
Q_INVOKABLE void sendRate()
Q_INVOKABLE void sendPitchAbsoluteYaw(float pitch, float yaw, bool showError=true)
Q_INVOKABLE void gimbalOnScreenControl(float panpct, float tiltpct, bool clickAndPoint, bool clickAndDrag, bool rateControl, bool retract=false, bool neutral=false, bool yawlock=false)
Q_INVOKABLE void acquireGimbalControl()
void setActiveGimbal(Gimbal *gimbal)
void gimbalYawStart(int direction)
Q_INVOKABLE void setGimbalRetract(bool set)
void showAcquireGimbalControlPopup()
void gimbalPitchStart(int direction)
Q_INVOKABLE void releaseGimbalControl()
Q_INVOKABLE void sendPitchBodyYaw(float pitch, float yaw, bool showError=true)
void setAbsolutePitch(float absPitch)
void setYawLock(bool yawLock)
void setPitchRate(float pitchRate)
void setManagerCompid(uint id)
void setAbsoluteYaw(float absYaw)
void setYawRate(float yawRate)
bool gimbalOthersHaveControl() const
void setGimbalHaveControl(bool set)
void setBodyYaw(float yaw)
void setDeviceId(uint id)
void setRetracted(bool retracted)
bool gimbalHaveControl() const
void setAbsoluteRoll(float absRoll)
void setGimbalOthersHaveControl(bool set)
void setCapabilityFlags(uint32_t flags)
static int getComponentId()
static MAVLinkProtocol * instance()
void append(QObject *object)
Caller maintains responsibility for object ownership and deletion.
static SettingsManager * instance()
GimbalControllerSettings * gimbalControllerSettings() const
WeakLinkInterfacePtr primaryLink() const
void sendMavCommand(int compId, MAV_CMD command, bool showError, float param1=0.0f, float param2=0.0f, float param3=0.0f, float param4=0.0f, float param5=0.0f, float param6=0.0f, float param7=0.0f)
void initialConnectComplete()
VehicleLinkManager * vehicleLinkManager()
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)
void mavlinkMessageReceived(const mavlink_message_t &message)