QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
RTCMMavlink.cc
Go to the documentation of this file.
1#include "RTCMMavlink.h"
2
3#include <QtCore/QByteArray>
4#include <QtCore/QSet>
5#include <QtCore/QThread>
6#include <algorithm>
7#include <cstring>
8
9#include "LinkInterface.h"
10#include "MAVLinkProtocol.h"
11#include "MultiVehicleManager.h"
12#include "QGCLoggingCategory.h"
13#include "QmlObjectListModel.h"
14#include "Vehicle.h"
15#include "VehicleLinkManager.h"
16
17QGC_LOGGING_CATEGORY(RTCMMavlinkLog, "GPS.RTCMMavlink")
18
19// Compile-time check that our constants match the MAVLink message definition.
20static_assert(RTCMMavlink::kFragmentLen == MAVLINK_MSG_GPS_RTCM_DATA_FIELD_DATA_LEN);
21
22RTCMMavlink::RTCMMavlink(QObject* parent) : QObject(parent)
23{
24 qCDebug(RTCMMavlinkLog) << this;
25}
26
28{
29 qCDebug(RTCMMavlinkLog) << this;
30}
31
32uint8_t RTCMMavlink::_makeFlags(bool fragmented, uint8_t fragmentId, uint8_t sequenceId)
33{
34 uint8_t flags = static_cast<uint8_t>((sequenceId & 0x1FU) << 3);
35 if (fragmented) {
36 flags |= 0x01U;
37 flags |= static_cast<uint8_t>((fragmentId & 0x03U) << 1);
38 }
39 return flags;
40}
41
42RTCMMavlink::PackResult RTCMMavlink::pack(QByteArrayView data, uint8_t sequenceId)
43{
44 PackResult result;
45 result.nextSequenceId = sequenceId;
46
47 if (data.isEmpty()) {
48 return result;
49 }
50
51 // Larger than the 4-fragment reassembly window: stream unfragmented chunks so
52 // the vehicle's RTCM framer can rebuild frames from the inject stream. Do not
53 // invent fragment IDs beyond 0..3 (would clobber the sequence field).
54 if (data.size() > kMaxAssembledLen) {
55 qsizetype start = 0;
56 while (start < data.size()) {
57 const qsizetype length = std::min(data.size() - start, kFragmentLen);
58 GpsRtcmPacket packet;
59 packet.flags = _makeFlags(false, 0, result.nextSequenceId);
60 packet.data = data.mid(start, length).toByteArray();
61 result.packets.append(std::move(packet));
62 ++result.nextSequenceId;
63 start += length;
64 }
65 return result;
66 }
67
68 if (data.size() <= kFragmentLen) {
69 GpsRtcmPacket packet;
70 packet.flags = _makeFlags(false, 0, sequenceId);
71 packet.data = data.toByteArray();
72 result.packets.append(std::move(packet));
73 ++result.nextSequenceId;
74 return result;
75 }
76
77 // Fragmented: 181..720 bytes. Fragment ID is only 2 bits (0..3).
78 uint8_t fragmentId = 0;
79 qsizetype start = 0;
80 while (start < data.size()) {
81 const qsizetype length = std::min(data.size() - start, kFragmentLen);
82 GpsRtcmPacket packet;
83 packet.flags = _makeFlags(true, fragmentId, sequenceId);
84 packet.data = data.mid(start, length).toByteArray();
85 result.packets.append(std::move(packet));
86 ++fragmentId;
87 start += length;
88 }
89
90 // Exact multiple of 180 with fewer than 4 fragments: MAVLink requires a final
91 // zero-length fragment so receivers know the message is complete. (All four
92 // full fragments complete by the "all fragments present" rule without this.)
93 // See ArduPilot AP_GPS::handle_gps_rtcm_fragment and PX4 GpsRtcmMessageAssembler.
94 if ((data.size() % kFragmentLen) == 0 && fragmentId < kMaxFragments) {
95 GpsRtcmPacket terminator;
96 terminator.flags = _makeFlags(true, fragmentId, sequenceId);
97 terminator.data.clear();
98 result.packets.append(std::move(terminator));
99 }
100
101 ++result.nextSequenceId;
102 return result;
103}
104
105void RTCMMavlink::RTCMDataUpdate(QByteArrayView data)
106{
107 if (data.isEmpty()) {
108 return;
109 }
110
111 _rateTracker.recordBytes(data.size());
112 if (_rateTracker.rateUpdated()) {
113 qCDebug(RTCMMavlinkLog) << QStringLiteral("RTCM bandwidth: %1 kB/s").arg(_rateTracker.kBps(), 0, 'f', 3);
114 emit bandwidthChanged();
115 }
116
117 const PackResult packed = pack(data, _sequenceId);
118 _sequenceId = packed.nextSequenceId;
119
120 for (const GpsRtcmPacket& packet : packed.packets) {
121 mavlink_gps_rtcm_data_t gpsRtcmData{};
122 gpsRtcmData.flags = packet.flags;
123 gpsRtcmData.len = static_cast<uint8_t>(packet.data.size());
124 if (!packet.data.isEmpty()) {
125 (void) memcpy(gpsRtcmData.data, packet.data.constData(), static_cast<size_t>(packet.data.size()));
126 }
127 _sendMessageOnAllLinks(gpsRtcmData);
128 }
129}
130
131void RTCMMavlink::sendSimulatedData(const std::atomic_bool& requestStop)
132{
133 constexpr int kMessageLengths[] = {30, 170, 240};
134 const QByteArray payload(kMessageLengths[2], '\0');
135 while (!requestStop) {
136 for (const int length : kMessageLengths) {
137 RTCMDataUpdate(QByteArrayView(payload).first(length));
138 QThread::msleep(4);
139 }
140 QThread::msleep(100);
141 }
142}
143
144void RTCMMavlink::_sendMessageOnAllLinks(const mavlink_gps_rtcm_data_t& data)
145{
147 QSet<const LinkInterface*> sentLinks;
148 for (qsizetype i = 0; i < vehicles->count(); i++) {
149 Vehicle* const vehicle = qobject_cast<Vehicle*>(vehicles->get(i));
150 if (!vehicle) {
151 continue;
152 }
153 const SharedLinkInterfacePtr sharedLink = vehicle->vehicleLinkManager()->primaryLink().lock();
154 if (!sharedLink || !sharedLink->isConnected()) {
155 continue;
156 }
157 // RTCM corrections are broadcast data. Send only once per link so that vehicles
158 // which share the same link don't cause duplicate sends. UDP links in particular
159 // send each write to all connected endpoints on the link. Send directly on the
160 // link rather than through a Vehicle so the send is not tied to whichever vehicle
161 // happens to be first on a shared link.
162 if (sentLinks.contains(sharedLink.get())) {
163 continue;
164 }
165 (void) sentLinks.insert(sharedLink.get());
166
167 mavlink_message_t message{};
168 (void) mavlink_msg_gps_rtcm_data_encode_chan(MAVLinkProtocol::instance()->getSystemId(),
169 MAVLinkProtocol::getComponentId(), sharedLink->mavlinkChannel(),
170 &message, &data);
171 sharedLink->sendMessageThreadSafe(message);
172 }
173}
std::shared_ptr< LinkInterface > SharedLinkInterfacePtr
struct __mavlink_message mavlink_message_t
#define QGC_LOGGING_CATEGORY(name, categoryStr)
double kBps() const
Current data rate in KB/s.
void recordBytes(qsizetype bytes)
Record incoming/outgoing bytes. Call whenever data passes through.
bool rateUpdated() const
static int getComponentId()
static MAVLinkProtocol * instance()
int getSystemId() const
static MultiVehicleManager * instance()
QmlObjectListModel * vehicles() const
Q_INVOKABLE QObject * get(int index)
int count() const override final
WeakLinkInterfacePtr primaryLink() const
VehicleLinkManager * vehicleLinkManager()
Definition Vehicle.h:579
uint8_t flags
Definition RTCMMavlink.h:17
QByteArray data
Definition RTCMMavlink.h:18