15#define MAVLINK_UNKNOWN_METERS -1000.0f
16#define MAVLINK_UNKNOWN_LAT 0
17#define MAVLINK_UNKNOWN_LON 0
18#define SENDING_RATE_MSEC 1000
19#define ALLOWED_GPS_DELAY 5000
20#define RID_TIMEOUT 2500
22const uint8_t* RemoteIDManager::_id_or_mac_unknown =
new uint8_t[MAVLINK_MSG_OPEN_DRONE_ID_OPERATOR_ID_FIELD_ID_OR_MAC_LEN]();
28 , _armStatusGood (false)
32 , _GCSBasicIDValid (false)
33 , _operatorIDGood (false)
34 , _emergencyDeclared (false)
36 , _targetComponent (0)
37 , _enforceSendingSelfID (false)
42 _odidTimeoutTimer.setSingleShot(
true);
44 connect(&_odidTimeoutTimer, &QTimer::timeout,
this, &RemoteIDManager::_odidTimeout);
48 connect(&_sendMessagesTimer, &QTimer::timeout,
this, &RemoteIDManager::_sendMessages);
56 connect(_settings->basicIDUaType(), &
Fact::rawValueChanged,
this, &RemoteIDManager::_checkGCSBasicID);
59 _targetSystem = _vehicle->
id();
60 _targetComponent = _vehicle->
compId();
62 if (_settings->operatorIDValid()->rawValue() ==
true || (_settings->region()->rawValue().toInt() !=
Region::EU && _settings->operatorID()->rawValue().toString().length() > 0)) {
65 _operatorIDGood =
true;
72 switch (message.msgid) {
74 case MAVLINK_MSG_ID_OPEN_DRONE_ID_ARM_STATUS:
75 _handleArmStatus(message);
82void RemoteIDManager::_odidTimeout()
85 _sendMessagesTimer.stop();
87 qCDebug(RemoteIDManagerLog) <<
"We stopped receiving heartbeat from RID device.";
94 if ( (message.compid < MAV_COMP_ID_ODID_TXRX_1) || (message.compid > MAV_COMP_ID_ODID_TXRX_3) ) {
96 if (message.compid != MAV_COMP_ID_AUTOPILOT1) {
102 if (_vehicle->
id() != message.sysid) {
109 qCDebug(RemoteIDManagerLog) <<
"Receiving ODID_ARM_STATUS for first time. Mavlink Open Drone ID support is available.";
113 if (_targetSystem != message.sysid) {
114 _targetSystem = message.sysid;
115 qCDebug(RemoteIDManagerLog) <<
"Subscribing to ODID messages coming from system " << _targetSystem;
120 _sendMessagesTimer.start();
123 qCDebug(RemoteIDManagerLog) <<
"Receiving ODID_ARM_STATUS from RID device";
127 _odidTimeoutTimer.start();
130 mavlink_open_drone_id_arm_status_t armStatus;
131 mavlink_msg_open_drone_id_arm_status_decode(&message, &armStatus);
133 if (armStatus.status == MAV_ODID_ARM_STATUS_GOOD_TO_ARM && !_armStatusGood) {
139 _armStatusGood =
true;
141 qCDebug(RemoteIDManagerLog) <<
"Arm status GOOD TO ARM.";
144 if (armStatus.status == MAV_ODID_ARM_STATUS_PRE_ARM_FAIL_GENERIC) {
145 _armStatusGood =
false;
146 _armStatusError = QString::fromLocal8Bit(armStatus.error);
148 if (armStatus.error == QString(
"missing basic_id message")) {
149 _basicIDGood =
false;
150 qCDebug(RemoteIDManagerLog) <<
"Arm status error, basic_id is not set in RID device nor in GCS!";
155 qCDebug(RemoteIDManagerLog) <<
"Arm status error:" << _armStatusError;
160void RemoteIDManager::_sendMessages()
166 if (_GCSBasicIDValid && _settings->sendBasicID()->rawValue().toBool()) {
172 if (_settings->sendSelfID()->rawValue().toBool() || _emergencyDeclared || _enforceSendingSelfID) {
178 if ((_settings->sendOperatorID()->rawValue().toBool() || (_settings->region()->rawValue().toInt() ==
Region::EU)) && _operatorIDGood) {
184void RemoteIDManager::_sendSelfIDMsg()
191 const QByteArray selfIdDescription = _getSelfIDDescription();
195 sharedLink->mavlinkChannel(),
200 _emergencyDeclared ? 1 : _settings->selfIDType()->rawValue().toInt(),
201 selfIdDescription.constData());
207QByteArray RemoteIDManager::_getSelfIDDescription()
const
209 QString descriptionToSend;
211 if (_emergencyDeclared) {
213 descriptionToSend = _settings->selfIDEmergency()->rawValue().toString();
215 switch (_settings->selfIDType()->rawValue().toInt()) {
217 descriptionToSend = _settings->selfIDFree()->rawValue().toString();
220 descriptionToSend = _settings->selfIDEmergency()->rawValue().toString();
223 descriptionToSend = _settings->selfIDExtended()->rawValue().toString();
226 descriptionToSend = _settings->selfIDEmergency()->rawValue().toString();
230 QByteArray descriptionBuffer = descriptionToSend.toLocal8Bit();
231 descriptionBuffer.resize(MAVLINK_MSG_OPEN_DRONE_ID_SELF_ID_FIELD_DESCRIPTION_LEN);
232 return descriptionBuffer;
235void RemoteIDManager::_sendOperatorID()
243 QByteArray bytesOperatorID = (_settings->operatorID()->rawValue().toString()).toLocal8Bit();
244 bytesOperatorID.resize(MAVLINK_MSG_OPEN_DRONE_ID_OPERATOR_ID_FIELD_OPERATOR_ID_LEN);
246 mavlink_msg_open_drone_id_operator_id_pack_chan(
249 sharedLink->mavlinkChannel(),
254 _settings->operatorIDType()->rawValue().toInt(),
255 bytesOperatorID.constData());
261void RemoteIDManager::_updateGcsGpsStatus(
bool gpsGood,
const QString&
error)
263 if (!
error.isEmpty() && _gcsGPSError !=
error) {
264 _gcsGPSError =
error;
265 qCWarning(RemoteIDManagerLog) <<
"GCS GPS error:" <<
error;
267 if (_gcsGPSGood != gpsGood) {
268 _gcsGPSGood = gpsGood;
270 _gcsGPSError.clear();
276void RemoteIDManager::_sendSystem()
278 QGeoCoordinate gcsPosition(0, 0, 0);
279 const uint32_t locationType = _settings->locationType()->rawValue().toUInt();
285 const double lat = _settings->latitudeFixed()->rawValue().toDouble();
286 const double lon = _settings->longitudeFixed()->rawValue().toDouble();
287 const double alt = _settings->altitudeFixed()->rawValue().toDouble();
290 if (lat >= -90.0 && lat <= 90.0 && lon >= -180.0 && lon <= 180.0) {
291 gcsPosition = QGeoCoordinate(lat, lon, alt);
292 _updateGcsGpsStatus(
true);
294 _updateGcsGpsStatus(
false,
"The provided coordinates for FIXED position are invalid.");
301 if (!geoPositionInfo.isValid()) {
302 _updateGcsGpsStatus(
false,
"GCS GPS data is not valid.");
304 _updateGcsGpsStatus(
false, QString(
"GCS GPS data error: %1").arg(positionManager->
gcsPositioningError()));
305 }
else if (!gcsPosition.isValid() || gcsPosition.type() == QGeoCoordinate::InvalidCoordinate) {
306 _updateGcsGpsStatus(
false,
"GCS GPS data error: Invalid coordinate type.");
307 }
else if (_settings->region()->rawValue().toInt() ==
Region::FAA && gcsPosition.type() != QGeoCoordinate::Coordinate3D) {
309 _updateGcsGpsStatus(
false,
"GCS GPS data error: Altitude data is mandatory for FAA regions.");
310 }
else if (_lastGeoPositionTimeStamp.msecsTo(QDateTime::currentDateTime().currentDateTimeUtc()) >
ALLOWED_GPS_DELAY) {
311 _updateGcsGpsStatus(
false,
"GCS GPS data is older than 5 seconds");
313 _updateGcsGpsStatus(
true);
325 sharedLink->mavlinkChannel(),
330 _settings->locationType()->rawValue().toUInt(),
331 _settings->classificationType()->rawValue().toUInt(),
338 _settings->categoryEU()->rawValue().toUInt(),
339 _settings->classEU()->rawValue().toUInt(),
342 _vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), msg);
347uint32_t RemoteIDManager::_timestamp2019()
349 uint32_t secsSinceEpoch2019 = 1546300800;
351 return ((QDateTime::currentDateTime().currentSecsSinceEpoch()) - secsSinceEpoch2019);
354void RemoteIDManager::_sendBasicID()
362 QString basicIDTemp = _settings->basicID()->rawValue().toString();
363 QByteArray ba = basicIDTemp.toLocal8Bit();
365 ba.resize(MAVLINK_MSG_OPEN_DRONE_ID_BASIC_ID_FIELD_UAS_ID_LEN);
369 sharedLink->mavlinkChannel(),
374 _settings->basicIDType()->rawValue().toUInt(),
375 _settings->basicIDUaType()->rawValue().toUInt(),
376 reinterpret_cast<const unsigned char*
>(ba.constData())),
382void RemoteIDManager::_checkGCSBasicID()
384 QString basicID = _settings->basicID()->rawValue().toString();
386 if (!basicID.isEmpty() && (_settings->basicIDType()->rawValue().toInt() >= 0) && (_settings->basicIDUaType()->rawValue().toInt() >= 0)) {
387 _GCSBasicIDValid =
true;
389 _GCSBasicIDValid =
false;
398 if (operatorID != _settings->operatorID()->rawValueString()) {
399 _settings->operatorIDValid()->setRawValue(_isEUOperatorIDValid(operatorID));
405 QString operatorID = _settings->operatorID()->rawValue().toString();
407 if (_settings->region()->rawValue().toInt() ==
Region::EU) {
411 _operatorIDGood = _settings->operatorIDValid()->rawValue() ==
true;
412 if (_operatorIDGood) {
414 _settings->operatorID()->setRawValue(operatorID.sliced(0, 16));
420 (!operatorID.isEmpty() && (_settings->operatorIDType()->rawValue().toInt() >= 0));
426bool RemoteIDManager::_isEUOperatorIDValid(
const QString& operatorID)
const
428 const bool containsDash = operatorID.contains(
'-');
429 if (!(operatorID.length() == 20 && containsDash) && !(operatorID.length() == 19 && !containsDash)) {
430 qCDebug(RemoteIDManagerLog) <<
"OperatorID not long enough";
434 const QString countryCode = operatorID.sliced(0,3);
435 if (!countryCode.isUpper()) {
436 qCDebug(RemoteIDManagerLog) <<
"OperatorID country code not uppercase";
440 const QString number = operatorID.sliced(3, 12);
441 const QChar checksum = operatorID.at(15);
442 const QString secret = containsDash ? operatorID.sliced(17, 3) : operatorID.sliced(16, 3);
443 const QString combination = number + secret;
445 const QChar result = _calculateLuhnMod36(combination);
447 const bool valid = (result == checksum);
448 qCDebug(RemoteIDManagerLog) <<
"Operator ID checksum " << (valid ?
"valid" :
"invalid");
452QChar RemoteIDManager::_calculateLuhnMod36(
const QString& input)
const {
454 const QString alphabet =
"0123456789abcdefghijklmnopqrstuvwxyz";
459 for (
int i = input.length() - 1; i >= 0; i--) {
460 int codePoint = alphabet.indexOf(input[i]);
461 int addend = factor * codePoint;
462 factor = (factor == 2) ? 1 : 2;
463 addend = (addend / n) + (addend % n);
467 int remainder = sum % n;
468 int checkCodePoint = (n - remainder) % n;
469 return alphabet.at(checkCodePoint);
474 _emergencyDeclared = declare;
479 _enforceSendingSelfID =
true;
481 qCDebug(RemoteIDManagerLog) << ( declare ?
"Emergency declared." :
"Emergency cleared.");
484void RemoteIDManager::_updateLastGCSPositionInfo(QGeoPositionInfo update)
486 if (update.isValid()) {
487 _lastGeoPositionTimeStamp = update.timestamp().toUTC();
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 ALLOWED_GPS_DELAY
#define MAVLINK_UNKNOWN_LON
void rawValueChanged(const QVariant &value)
static int getComponentId()
static MAVLinkProtocol * instance()
QGeoPositionInfo geoPositionInfo() const
static QGCPositionManager * instance()
QGeoCoordinate gcsPosition() const
QGeoPositionInfoSource::Error gcsPositioningError() const
void positionInfoUpdated(QGeoPositionInfo update)
void mavlinkMessageReceived(mavlink_message_t &message)
void operatorIDGoodChanged()
Q_INVOKABLE void setEmergency(bool declare)
void armStatusErrorChanged()
void basicIDGoodChanged()
Q_INVOKABLE void setOperatorID()
void emergencyDeclaredChanged()
void armStatusGoodChanged()
RemoteIDManager(Vehicle *vehicle)
true: the vehicle supports Mavlink Open Drone ID messages
Q_INVOKABLE void checkOperatorID(const QString &operatorID)
static SettingsManager * instance()
RemoteIDSettings * remoteIDSettings() const
WeakLinkInterfacePtr primaryLink() const
VehicleLinkManager * vehicleLinkManager()
bool sendMessageOnLinkThreadSafe(LinkInterface *link, mavlink_message_t message)