19#define MAVLINK_UNKNOWN_METERS -1000.0f
20#define MAVLINK_UNKNOWN_LAT 0
21#define MAVLINK_UNKNOWN_LON 0
22#define SENDING_RATE_MSEC 1000
23#define RID_TIMEOUT 2500
25const uint8_t* RemoteIDManager::_id_or_mac_unknown =
new uint8_t[MAVLINK_MSG_OPEN_DRONE_ID_OPERATOR_ID_FIELD_ID_OR_MAC_LEN]();
31 , _armStatusGoodToArm (false)
32 , _ridDeviceCommsGood (false)
33 , _gcsPositionUsable (false)
34 , _vehicleReportsBasicIDMissing(false)
35 , _emergencyDeclared (false)
37 , _targetComponent (0)
38 , _enforceSendingSelfID (false)
43 _odidTimeoutTimer.setSingleShot(
true);
45 connect(&_odidTimeoutTimer, &QTimer::timeout,
this, &RemoteIDManager::_odidTimeout);
49 connect(&_sendMessagesTimer, &QTimer::timeout,
this, &RemoteIDManager::_sendMessages);
52 _targetSystem = _vehicle->
id();
53 _targetComponent = _vehicle->
compId();
58 switch (message.msgid) {
60 case MAVLINK_MSG_ID_OPEN_DRONE_ID_ARM_STATUS:
61 _handleArmStatus(message);
68void RemoteIDManager::_odidTimeout()
70 _ridDeviceCommsGood =
false;
71 _sendMessagesTimer.stop();
73 qCDebug(RemoteIDManagerLog) <<
"We stopped receiving heartbeat from RID device.";
80 if ( (message.compid < MAV_COMP_ID_ODID_TXRX_1) || (message.compid > MAV_COMP_ID_ODID_TXRX_3) ) {
82 if (message.compid != MAV_COMP_ID_AUTOPILOT1) {
88 if (_vehicle->
id() != message.sysid) {
95 qCDebug(RemoteIDManagerLog) <<
"Receiving ODID_ARM_STATUS for first time. Mavlink Open Drone ID support is available.";
99 if (_targetSystem != message.sysid) {
100 _targetSystem = message.sysid;
101 qCDebug(RemoteIDManagerLog) <<
"Subscribing to ODID messages coming from system " << _targetSystem;
104 if (!_ridDeviceCommsGood) {
105 _ridDeviceCommsGood =
true;
106 _sendMessagesTimer.start();
108 qCDebug(RemoteIDManagerLog) <<
"Receiving ODID_ARM_STATUS from RID device";
112 _odidTimeoutTimer.start();
115 mavlink_open_drone_id_arm_status_t armStatus;
116 mavlink_msg_open_drone_id_arm_status_decode(&message, &armStatus);
118 if (armStatus.status == MAV_ODID_ARM_STATUS_GOOD_TO_ARM) {
120 if (_vehicleReportsBasicIDMissing) {
121 _vehicleReportsBasicIDMissing =
false;
125 if (!_armStatusError.isEmpty()) {
126 _armStatusError.clear();
129 if (!_armStatusGoodToArm) {
130 _armStatusGoodToArm =
true;
132 qCDebug(RemoteIDManagerLog) <<
"Arm status GOOD TO ARM.";
136 if (armStatus.status == MAV_ODID_ARM_STATUS_PRE_ARM_FAIL_GENERIC) {
137 if (_armStatusGoodToArm) {
138 _armStatusGoodToArm =
false;
142 const QString
armStatusError = QString::fromUtf8(armStatus.error, qstrnlen(armStatus.error,
sizeof(armStatus.error)));
145 const bool basicIDMissing = (
armStatusError == QStringLiteral(
"missing basic_id message"));
146 if (_vehicleReportsBasicIDMissing != basicIDMissing) {
147 _vehicleReportsBasicIDMissing = basicIDMissing;
148 if (basicIDMissing) {
149 qCDebug(RemoteIDManagerLog) <<
"Arm status error, basic_id is not set in RID device nor in GCS!";
156 qCDebug(RemoteIDManagerLog) <<
"Arm status error:" << _armStatusError;
162void RemoteIDManager::_sendMessages()
168 if (_settings->
basicIDValid() && _settings->sendBasicID()->rawValue().toBool()) {
174 if (_settings->sendSelfID()->rawValue().toBool() || _emergencyDeclared || _enforceSendingSelfID) {
186void RemoteIDManager::_sendSelfIDMsg()
193 const QByteArray selfIdDescription = _getSelfIDDescription();
197 sharedLink->mavlinkChannel(),
202 _emergencyDeclared ? 1 : _settings->selfIDType()->rawValue().toInt(),
203 selfIdDescription.constData());
209QByteArray RemoteIDManager::_getSelfIDDescription()
const
211 QString descriptionToSend;
213 if (_emergencyDeclared) {
215 descriptionToSend = _settings->selfIDEmergency()->rawValue().toString();
217 switch (_settings->selfIDType()->rawValue().toInt()) {
219 descriptionToSend = _settings->selfIDFree()->rawValue().toString();
222 descriptionToSend = _settings->selfIDEmergency()->rawValue().toString();
225 descriptionToSend = _settings->selfIDExtended()->rawValue().toString();
228 descriptionToSend = _settings->selfIDEmergency()->rawValue().toString();
232 QByteArray descriptionBuffer = descriptionToSend.toLocal8Bit();
233 descriptionBuffer.resize(MAVLINK_MSG_OPEN_DRONE_ID_SELF_ID_FIELD_DESCRIPTION_LEN,
'\0');
234 return descriptionBuffer;
237void RemoteIDManager::_sendOperatorID()
247 Fact*
const operatorIDFact = isEURegion ? _settings->operatorIDEU() : _settings->operatorIDFAA();
248 QByteArray bytesOperatorID = operatorIDFact->
rawValue().toString().toLocal8Bit();
249 bytesOperatorID.resize(MAVLINK_MSG_OPEN_DRONE_ID_OPERATOR_ID_FIELD_OPERATOR_ID_LEN,
'\0');
251 mavlink_msg_open_drone_id_operator_id_pack_chan(
254 sharedLink->mavlinkChannel(),
259 _settings->operatorIDType()->rawValue().toInt(),
260 bytesOperatorID.constData());
266void RemoteIDManager::_updateGcsPositionStatus(
bool usable,
const QString&
error)
268 if (!
error.isEmpty() && _gcsPositionError !=
error) {
269 _gcsPositionError =
error;
270 qCWarning(RemoteIDManagerLog) <<
"GCS GPS error:" <<
error;
272 if (_gcsPositionUsable != usable) {
273 _gcsPositionUsable = usable;
275 _gcsPositionError.clear();
281void RemoteIDManager::_sendSystem()
283 QGeoCoordinate gcsPosition(0, 0, 0);
284 const uint32_t locationType = _settings->locationType()->rawValue().toUInt();
290 const double lat = _settings->latitudeFixed()->rawValue().toDouble();
291 const double lon = _settings->longitudeFixed()->rawValue().toDouble();
292 const double alt = _settings->altitudeFixed()->rawValue().toDouble();
295 if (lat >= -90.0 && lat <= 90.0 && lon >= -180.0 && lon <= 180.0) {
296 gcsPosition = QGeoCoordinate(lat, lon, alt);
297 _updateGcsPositionStatus(
true);
299 _updateGcsPositionStatus(
false,
"The provided coordinates for FIXED position are invalid.");
306 gcsPosition = observation->position.coordinate();
308 const bool notGeodetic =
310 if (notGeodetic && !std::exchange(_gcsAltitudeNotGeodeticReported,
true)) {
311 qCWarning(RemoteIDManagerLog)
312 <<
"GCS altitude is not a WGS84 ellipsoid height; sending it as the operator's geodetic altitude";
313 }
else if (!notGeodetic) {
314 _gcsAltitudeNotGeodeticReported =
false;
318 const auto sourceStatus = positionManager->
sourceStatus();
320 if (positioningError != QGeoPositionInfoSource::NoError &&
321 positioningError != QGeoPositionInfoSource::UpdateTimeoutError) {
322 _updateGcsPositionStatus(
false, QString(
"GCS GPS data error: %1").arg(positioningError));
326 _updateGcsPositionStatus(
false, waiting ? QString() : positionManager->sourceStatusText());
327 }
else if (!observation) {
328 _updateGcsPositionStatus(
false, QStringLiteral(
"GCS GPS data is not valid."));
329 }
else if (!gcsPosition.isValid() || gcsPosition.type() == QGeoCoordinate::InvalidCoordinate) {
330 _updateGcsPositionStatus(
false,
"GCS GPS data error: Invalid coordinate type.");
331 }
else if (_settings->region()->rawValue().toInt() ==
333 gcsPosition.type() != QGeoCoordinate::Coordinate3D) {
334 _updateGcsPositionStatus(
false,
"GCS GPS data error: Altitude data is mandatory for FAA regions.");
336 _updateGcsPositionStatus(
true);
346 mavlink_msg_open_drone_id_system_pack_chan(
348 &msg, _targetSystem, _targetComponent, _id_or_mac_unknown, _settings->locationType()->rawValue().toUInt(),
349 _settings->classificationType()->rawValue().toUInt(),
353 _settings->classEU()->rawValue().toUInt(),
354 _gcsPositionUsable && qIsFinite(gcsPosition.altitude()) ? gcsPosition.altitude() :
MAVLINK_UNKNOWN_METERS,
361uint32_t RemoteIDManager::_timestamp2019()
363 uint32_t secsSinceEpoch2019 = 1546300800;
365 return ((QDateTime::currentDateTime().currentSecsSinceEpoch()) - secsSinceEpoch2019);
368void RemoteIDManager::_sendBasicID()
376 QString basicIDTemp = _settings->basicID()->rawValue().toString();
377 QByteArray ba = basicIDTemp.toLocal8Bit();
379 ba.resize(MAVLINK_MSG_OPEN_DRONE_ID_BASIC_ID_FIELD_UAS_ID_LEN,
'\0');
383 sharedLink->mavlinkChannel(),
388 _settings->basicIDType()->rawValue().toUInt(),
389 _settings->basicIDUaType()->rawValue().toUInt(),
390 reinterpret_cast<const unsigned char*
>(ba.constData())),
398 _emergencyDeclared = declare;
403 _enforceSendingSelfID =
true;
405 qCDebug(RemoteIDManagerLog) << ( declare ?
"Emergency declared." :
"Emergency cleared.");
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
std::weak_ptr< LinkInterface > WeakLinkInterfacePtr
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
#define MAVLINK_UNKNOWN_LAT
#define SENDING_RATE_MSEC
#define MAVLINK_UNKNOWN_METERS
#define MAVLINK_UNKNOWN_LON
A Fact is used to hold a single value within the system.
QVariant rawValue() const
static GPSManager * instance()
PositionManager * positionManager() const
static constexpr std::chrono::milliseconds FRESHNESS_TIMEOUT
Default position age at which health becomes Stale and the position stops being usable.
static int getComponentId()
static MAVLinkProtocol * instance()
std::optional< GPSObservation > acceptedObservation(GPSObservation::PositionUse use=GPSObservation::PositionUse::GroundStation, std::optional< std::chrono::milliseconds > maximumAge=std::nullopt) const
SourceStatus sourceStatus() const
QGeoPositionInfoSource::Error gcsPositioningError() const
void mavlinkMessageReceived(mavlink_message_t &message)
void vehicleReportsBasicIDMissingChanged()
Q_INVOKABLE void setEmergency(bool declare)
void armStatusErrorChanged()
QString armStatusError(void) const
void ridDeviceCommsGoodChanged()
void armStatusGoodToArmChanged()
void emergencyDeclaredChanged()
RemoteIDManager(Vehicle *vehicle)
true: RID device reports MAV_ODID_ARM_STATUS_GOOD_TO_ARM
void gcsPositionUsableChanged()
bool operatorIDValidForRegion() const
bool basicIDValid() const
true: the basic ID entered in settings is complete enough to broadcast
static SettingsManager * instance()
RemoteIDSettings * remoteIDSettings() const
WeakLinkInterfacePtr primaryLink() const
VehicleLinkManager * vehicleLinkManager()
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)