• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mavlink / MAVSDK / 17565334070

08 Sep 2025 10:02PM UTC coverage: 47.262% (+0.02%) from 47.241%
17565334070

Pull #2649

github

web-flow
Merge 31bace7d2 into 57e1189fd
Pull Request #2649: CI: Tell sonar we use C++17

16788 of 35521 relevant lines covered (47.26%)

444.32 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.6
/src/system_tests/mavlink_direct.cpp
1
#include "log.h"
2
#include "mavsdk.h"
3
#include "plugins/mavlink_direct/mavlink_direct.h"
4
#include "plugins/telemetry/telemetry.h"
5
#include "plugins/telemetry_server/telemetry_server.h"
6
#include <chrono>
7
#include <thread>
8
#include <future>
9
#include <gtest/gtest.h>
10
#include <json/json.h>
11

12
using namespace mavsdk;
13

14
TEST(SystemTest, MavlinkDirectRoundtrip)
4✔
15
{
16
    Mavsdk mavsdk_groundstation{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
17
    Mavsdk mavsdk_autopilot{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
18

19
    ASSERT_EQ(
1✔
20
        mavsdk_groundstation.add_any_connection("udpin://0.0.0.0:17000"),
21
        ConnectionResult::Success);
1✔
22
    ASSERT_EQ(
1✔
23
        mavsdk_autopilot.add_any_connection("udpout://127.0.0.1:17000"), ConnectionResult::Success);
1✔
24

25
    // Ground station discovers the autopilot system
26
    auto maybe_system = mavsdk_groundstation.first_autopilot(10.0);
1✔
27
    ASSERT_TRUE(maybe_system);
1✔
28
    auto system = maybe_system.value();
1✔
29
    ASSERT_TRUE(system->has_autopilot());
1✔
30

31
    // Wait for autopilot instance to discover the connection to the ground station
32
    LogInfo() << "Waiting for autopilot system to connect...";
1✔
33
    while (mavsdk_autopilot.systems().size() == 0) {
1✔
34
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
×
35
    }
36

37
    // Get the autopilot's view of the connected system
38
    auto groundstation_system = mavsdk_autopilot.systems().at(0);
1✔
39
    ASSERT_EQ(groundstation_system->component_ids()[0], 190);
2✔
40

41
    // Create separate MavlinkDirect instances for sender (autopilot) and receiver (ground station)
42
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
43
    auto sender_mavlink_direct = MavlinkDirect{groundstation_system};
1✔
44

45
    auto prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
46
    auto fut = prom.get_future();
1✔
47

48
    // Ground station subscribes to receive GLOBAL_POSITION_INT from autopilot
49
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
50
        "GLOBAL_POSITION_INT", [&prom](MavlinkDirect::MavlinkMessage message) {
2✔
51
            LogInfo() << "Received GLOBAL_POSITION_INT: " << message.fields_json;
1✔
52
            prom.set_value(message);
1✔
53
        });
1✔
54

55
    // Wait a bit more for subscription to be properly registered
56
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
57

58
    // Create a complex test message with multiple field types
59
    MavlinkDirect::MavlinkMessage test_message;
1✔
60
    test_message.message_name = "GLOBAL_POSITION_INT";
1✔
61
    test_message.system_id = 1;
1✔
62
    test_message.component_id = 1;
1✔
63
    test_message.target_system_id = 0;
1✔
64
    test_message.target_component_id = 0;
1✔
65
    test_message.fields_json =
66
        R"({"time_boot_ms":12345,"lat":473977418,"lon":-1223974560,"alt":100500,"relative_alt":50250,"vx":100,"vy":-50,"vz":25,"hdg":18000})";
1✔
67

68
    LogInfo() << "Sending GLOBAL_POSITION_INT message...";
1✔
69
    auto result = sender_mavlink_direct.send_message(test_message);
1✔
70
    LogInfo() << "Send result: "
1✔
71
              << (result == MavlinkDirect::Result::Success ? "Success" : "Error");
1✔
72
    EXPECT_EQ(result, MavlinkDirect::Result::Success);
1✔
73

74
    // Wait for message to be received
75
    ASSERT_EQ(fut.wait_for(std::chrono::seconds(1)), std::future_status::ready);
1✔
76

77
    auto received_message = fut.get();
2✔
78

79
    EXPECT_EQ(received_message.message_name, test_message.message_name);
1✔
80
    EXPECT_EQ(received_message.system_id, test_message.system_id);
1✔
81
    EXPECT_EQ(received_message.component_id, test_message.component_id);
1✔
82

83
    // Note: For now we only check message_name since field extraction is simplified
84
    EXPECT_TRUE(received_message.fields_json.find("GLOBAL_POSITION_INT") != std::string::npos);
1✔
85

86
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
87
}
1✔
88

89
TEST(SystemTest, MavlinkDirectExtendedFields)
4✔
90
{
91
    Mavsdk mavsdk_groundstation{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
92
    Mavsdk mavsdk_autopilot{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
93

94
    ASSERT_EQ(
1✔
95
        mavsdk_groundstation.add_any_connection("udpin://0.0.0.0:17001"),
96
        ConnectionResult::Success);
1✔
97
    ASSERT_EQ(
1✔
98
        mavsdk_autopilot.add_any_connection("udpout://127.0.0.1:17001"), ConnectionResult::Success);
1✔
99

100
    // Ground station discovers the autopilot system
101
    auto maybe_system = mavsdk_groundstation.first_autopilot(10.0);
1✔
102
    ASSERT_TRUE(maybe_system);
1✔
103
    auto system = maybe_system.value();
1✔
104
    ASSERT_TRUE(system->has_autopilot());
1✔
105

106
    // Wait for autopilot instance to discover the connection to the ground station
107
    while (mavsdk_autopilot.systems().size() == 0) {
2✔
108
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
109
    }
110

111
    auto groundstation_system = mavsdk_autopilot.systems().at(0);
1✔
112
    ASSERT_EQ(groundstation_system->component_ids()[0], 190);
2✔
113

114
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
115
    auto sender_mavlink_direct = MavlinkDirect{groundstation_system};
1✔
116

117
    auto compact_prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
118
    auto compact_fut = compact_prom.get_future();
1✔
119
    auto full_prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
120
    auto full_fut = full_prom.get_future();
1✔
121

122
    bool compact_received = false;
1✔
123

124
    // Subscribe to SYS_STATUS messages
125
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
126
        "SYS_STATUS", [&](MavlinkDirect::MavlinkMessage message) {
2✔
127
            LogInfo() << "Received SYS_STATUS: " << message.fields_json;
2✔
128
            if (!compact_received) {
2✔
129
                compact_received = true;
1✔
130
                compact_prom.set_value(message);
1✔
131
            } else {
132
                full_prom.set_value(message);
1✔
133
            }
134
        });
2✔
135

136
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
137

138
    // Test 1: Send SYS_STATUS in compact form (no extensions)
139
    MavlinkDirect::MavlinkMessage compact_message;
1✔
140
    compact_message.message_name = "SYS_STATUS";
1✔
141
    compact_message.system_id = 1;
1✔
142
    compact_message.component_id = 1;
1✔
143
    compact_message.target_system_id = 0;
1✔
144
    compact_message.target_component_id = 0;
1✔
145
    compact_message.fields_json =
146
        R"({"onboard_control_sensors_present":1,"onboard_control_sensors_enabled":1,"onboard_control_sensors_health":1,"load":500,"voltage_battery":12000,"current_battery":1000,"battery_remaining":75,"drop_rate_comm":0,"errors_comm":0,"errors_count1":0,"errors_count2":0,"errors_count3":0,"errors_count4":0})";
1✔
147

148
    auto result1 = sender_mavlink_direct.send_message(compact_message);
1✔
149
    EXPECT_EQ(result1, MavlinkDirect::Result::Success);
1✔
150

151
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
1✔
152

153
    // Test 2: Send SYS_STATUS in full form (with extensions)
154
    MavlinkDirect::MavlinkMessage full_message;
1✔
155
    full_message.message_name = "SYS_STATUS";
1✔
156
    full_message.system_id = 1;
1✔
157
    full_message.component_id = 1;
1✔
158
    full_message.target_system_id = 0;
1✔
159
    full_message.target_component_id = 0;
1✔
160
    full_message.fields_json =
161
        R"({"onboard_control_sensors_present":1,"onboard_control_sensors_enabled":1,"onboard_control_sensors_health":1,"load":500,"voltage_battery":12000,"current_battery":1000,"battery_remaining":75,"drop_rate_comm":0,"errors_comm":0,"errors_count1":0,"errors_count2":0,"errors_count3":0,"errors_count4":0,"onboard_control_sensors_present_extended":123,"onboard_control_sensors_enabled_extended":456,"onboard_control_sensors_health_extended":789})";
1✔
162

163
    auto result2 = sender_mavlink_direct.send_message(full_message);
1✔
164
    EXPECT_EQ(result2, MavlinkDirect::Result::Success);
1✔
165

166
    // Wait for both messages to be received
167
    ASSERT_EQ(compact_fut.wait_for(std::chrono::seconds(5)), std::future_status::ready);
1✔
168
    ASSERT_EQ(full_fut.wait_for(std::chrono::seconds(5)), std::future_status::ready);
1✔
169

170
    auto received_compact = compact_fut.get();
1✔
171
    auto received_full = full_fut.get();
1✔
172

173
    // Verify both messages were received and parsing worked
174
    EXPECT_EQ(received_compact.message_name, "SYS_STATUS");
1✔
175
    EXPECT_EQ(received_full.message_name, "SYS_STATUS");
1✔
176

177
    // Parse JSON to verify field values
178
    Json::Value compact_json, full_json;
1✔
179
    Json::Reader reader;
1✔
180

181
    ASSERT_TRUE(reader.parse(received_compact.fields_json, compact_json));
1✔
182
    ASSERT_TRUE(reader.parse(received_full.fields_json, full_json));
1✔
183

184
    // Verify basic fields are present and correct in both messages
185
    EXPECT_EQ(compact_json["onboard_control_sensors_present"].asUInt(), 1u);
1✔
186
    EXPECT_EQ(compact_json["load"].asUInt(), 500u);
1✔
187
    EXPECT_EQ(compact_json["voltage_battery"].asUInt(), 12000u);
1✔
188
    EXPECT_EQ(compact_json["current_battery"].asInt(), 1000);
1✔
189
    EXPECT_EQ(compact_json["battery_remaining"].asInt(), 75);
1✔
190

191
    EXPECT_EQ(full_json["onboard_control_sensors_present"].asUInt(), 1u);
1✔
192
    EXPECT_EQ(full_json["load"].asUInt(), 500u);
1✔
193
    EXPECT_EQ(full_json["voltage_battery"].asUInt(), 12000u);
1✔
194
    EXPECT_EQ(full_json["current_battery"].asInt(), 1000);
1✔
195
    EXPECT_EQ(full_json["battery_remaining"].asInt(), 75);
1✔
196

197
    // Verify compact message HAS extended fields with zero values (MAVLink v2 zero-truncation)
198
    EXPECT_TRUE(compact_json.isMember("onboard_control_sensors_present_extended"));
1✔
199
    EXPECT_TRUE(compact_json.isMember("onboard_control_sensors_enabled_extended"));
1✔
200
    EXPECT_TRUE(compact_json.isMember("onboard_control_sensors_health_extended"));
1✔
201
    EXPECT_EQ(compact_json["onboard_control_sensors_present_extended"].asUInt(), 0u);
1✔
202
    EXPECT_EQ(compact_json["onboard_control_sensors_enabled_extended"].asUInt(), 0u);
1✔
203
    EXPECT_EQ(compact_json["onboard_control_sensors_health_extended"].asUInt(), 0u);
1✔
204

205
    // Verify full message HAS extended fields with correct values
206
    EXPECT_TRUE(full_json.isMember("onboard_control_sensors_present_extended"));
1✔
207
    EXPECT_TRUE(full_json.isMember("onboard_control_sensors_enabled_extended"));
1✔
208
    EXPECT_TRUE(full_json.isMember("onboard_control_sensors_health_extended"));
1✔
209

210
    EXPECT_EQ(full_json["onboard_control_sensors_present_extended"].asUInt(), 123u);
1✔
211
    EXPECT_EQ(full_json["onboard_control_sensors_enabled_extended"].asUInt(), 456u);
1✔
212
    EXPECT_EQ(full_json["onboard_control_sensors_health_extended"].asUInt(), 789u);
1✔
213

214
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
215
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
216
}
1✔
217

218
TEST(SystemTest, MavlinkDirectToTelemetry)
4✔
219
{
220
    Mavsdk mavsdk_groundstation{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
221
    Mavsdk mavsdk_autopilot{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
222

223
    ASSERT_EQ(
1✔
224
        mavsdk_groundstation.add_any_connection("udpin://0.0.0.0:17002"),
225
        ConnectionResult::Success);
1✔
226
    ASSERT_EQ(
1✔
227
        mavsdk_autopilot.add_any_connection("udpout://127.0.0.1:17002"), ConnectionResult::Success);
1✔
228

229
    // Ground station discovers the autopilot system
230
    auto maybe_system = mavsdk_groundstation.first_autopilot(10.0);
1✔
231
    ASSERT_TRUE(maybe_system);
1✔
232
    auto system = maybe_system.value();
1✔
233
    ASSERT_TRUE(system->has_autopilot());
1✔
234

235
    // Wait for autopilot instance to discover the connection to the ground station
236
    while (mavsdk_autopilot.systems().size() == 0) {
2✔
237
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
238
    }
239

240
    auto groundstation_system = mavsdk_autopilot.systems().at(0);
1✔
241
    ASSERT_EQ(groundstation_system->component_ids()[0], 190);
2✔
242

243
    // Autopilot uses MavlinkDirect to send, Ground station uses Telemetry to receive
244
    auto sender_mavlink_direct = MavlinkDirect{groundstation_system};
1✔
245
    auto receiver_telemetry = Telemetry{system};
1✔
246

247
    auto prom = std::promise<Telemetry::Position>();
1✔
248
    auto fut = prom.get_future();
1✔
249

250
    // Subscribe to position updates via Telemetry
251
    auto handle = receiver_telemetry.subscribe_position([&prom](Telemetry::Position position) {
3✔
252
        LogInfo() << "Received position via Telemetry: lat=" << position.latitude_deg
2✔
253
                  << " lon=" << position.longitude_deg;
1✔
254
        prom.set_value(position);
1✔
255
    });
1✔
256

257
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
258

259
    // Send GLOBAL_POSITION_INT via MavlinkDirect
260
    MavlinkDirect::MavlinkMessage test_message;
1✔
261
    test_message.message_name = "GLOBAL_POSITION_INT";
1✔
262
    test_message.system_id = 1;
1✔
263
    test_message.component_id = 1;
1✔
264
    test_message.target_system_id = 0;
1✔
265
    test_message.target_component_id = 0;
1✔
266
    test_message.fields_json =
267
        R"({"time_boot_ms":12345,"lat":473977418,"lon":-1223974560,"alt":100500,"relative_alt":50250,"vx":100,"vy":-50,"vz":25,"hdg":18000})";
1✔
268

269
    auto result = sender_mavlink_direct.send_message(test_message);
1✔
270
    EXPECT_EQ(result, MavlinkDirect::Result::Success);
1✔
271

272
    // Wait for position to be received
273
    ASSERT_EQ(fut.wait_for(std::chrono::seconds(5)), std::future_status::ready);
1✔
274

275
    auto received_position = fut.get();
1✔
276

277
    // Verify the position data (lat/lon are in degrees * 1e7 in MAVLink)
278
    EXPECT_NEAR(received_position.latitude_deg, 47.3977418, 0.0001);
1✔
279
    EXPECT_NEAR(received_position.longitude_deg, -122.3974560, 0.0001);
1✔
280

281
    receiver_telemetry.unsubscribe_position(handle);
1✔
282
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
283
}
1✔
284

285
TEST(SystemTest, TelemetryServerToMavlinkDirect)
4✔
286
{
287
    Mavsdk mavsdk_groundstation{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
288
    Mavsdk mavsdk_autopilot{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
289

290
    ASSERT_EQ(
1✔
291
        mavsdk_groundstation.add_any_connection("udpin://0.0.0.0:17003"),
292
        ConnectionResult::Success);
1✔
293
    ASSERT_EQ(
1✔
294
        mavsdk_autopilot.add_any_connection("udpout://127.0.0.1:17003"), ConnectionResult::Success);
1✔
295

296
    // Ground station discovers the autopilot system
297
    auto maybe_system = mavsdk_groundstation.first_autopilot(10.0);
1✔
298
    ASSERT_TRUE(maybe_system);
1✔
299
    auto system = maybe_system.value();
1✔
300
    ASSERT_TRUE(system->has_autopilot());
1✔
301

302
    // Wait for autopilot instance to discover the connection to the ground station
303
    while (mavsdk_autopilot.systems().size() == 0) {
2✔
304
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
305
    }
306

307
    auto groundstation_system = mavsdk_autopilot.systems().at(0);
1✔
308
    ASSERT_EQ(groundstation_system->component_ids()[0], 190);
2✔
309

310
    // Autopilot uses TelemetryServer to send, Ground station uses MavlinkDirect to receive
311
    auto autopilot_server_component = mavsdk_autopilot.server_component();
1✔
312
    auto sender_telemetry_server = TelemetryServer{autopilot_server_component};
1✔
313
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
314

315
    auto prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
316
    auto fut = prom.get_future();
1✔
317

318
    // Subscribe to GLOBAL_POSITION_INT via MavlinkDirect
319
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
320
        "GLOBAL_POSITION_INT", [&prom](MavlinkDirect::MavlinkMessage message) {
2✔
321
            LogInfo() << "Received GLOBAL_POSITION_INT via MavlinkDirect: " << message.fields_json;
1✔
322
            prom.set_value(message);
1✔
323
        });
1✔
324

325
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
326

327
    // Send position via TelemetryServer
328
    TelemetryServer::Position position;
1✔
329
    position.latitude_deg = 47.3977418;
1✔
330
    position.longitude_deg = -122.3974560;
1✔
331
    position.absolute_altitude_m = 100.5;
1✔
332
    position.relative_altitude_m = 50.25;
1✔
333

334
    TelemetryServer::VelocityNed velocity_ned;
1✔
335
    velocity_ned.north_m_s = 1.0f;
1✔
336
    velocity_ned.east_m_s = -0.5f;
1✔
337
    velocity_ned.down_m_s = 0.25f;
1✔
338

339
    TelemetryServer::Heading heading;
1✔
340
    heading.heading_deg = 180.0f;
1✔
341

342
    auto result = sender_telemetry_server.publish_position(position, velocity_ned, heading);
1✔
343
    EXPECT_EQ(result, TelemetryServer::Result::Success);
1✔
344

345
    // Wait for message to be received
346
    ASSERT_EQ(fut.wait_for(std::chrono::seconds(5)), std::future_status::ready);
1✔
347

348
    auto received_message = fut.get();
1✔
349

350
    EXPECT_EQ(received_message.message_name, "GLOBAL_POSITION_INT");
1✔
351
    EXPECT_EQ(received_message.system_id, 1);
1✔
352
    EXPECT_EQ(received_message.component_id, 1);
1✔
353

354
    // Verify the JSON contains the message info (since we're using simplified parsing)
355
    EXPECT_TRUE(received_message.fields_json.find("GLOBAL_POSITION_INT") != std::string::npos);
1✔
356

357
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
358
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
359
}
1✔
360

361
TEST(SystemTest, MavlinkDirectArrayFields)
4✔
362
{
363
    Mavsdk mavsdk_groundstation{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
364
    Mavsdk mavsdk_autopilot{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
365

366
    ASSERT_EQ(
1✔
367
        mavsdk_groundstation.add_any_connection("udpin://0.0.0.0:17004"),
368
        ConnectionResult::Success);
1✔
369
    ASSERT_EQ(
1✔
370
        mavsdk_autopilot.add_any_connection("udpout://127.0.0.1:17004"), ConnectionResult::Success);
1✔
371

372
    // Ground station discovers the autopilot system
373
    auto maybe_system = mavsdk_groundstation.first_autopilot(10.0);
1✔
374
    ASSERT_TRUE(maybe_system);
1✔
375
    auto system = maybe_system.value();
1✔
376
    ASSERT_TRUE(system->has_autopilot());
1✔
377

378
    // Wait for autopilot instance to discover the connection to the ground station
379
    while (mavsdk_autopilot.systems().size() == 0) {
2✔
380
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
381
    }
382

383
    auto groundstation_system = mavsdk_autopilot.systems().at(0);
1✔
384
    ASSERT_EQ(groundstation_system->component_ids()[0], 190);
2✔
385

386
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
387
    auto sender_mavlink_direct = MavlinkDirect{groundstation_system};
1✔
388

389
    auto partial_prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
390
    auto partial_fut = partial_prom.get_future();
1✔
391
    auto full_prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
392
    auto full_fut = full_prom.get_future();
1✔
393

394
    bool partial_received = false;
1✔
395

396
    // Subscribe to GPS_STATUS messages
397
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
398
        "GPS_STATUS", [&](MavlinkDirect::MavlinkMessage message) {
2✔
399
            LogInfo() << "Received GPS_STATUS: " << message.fields_json;
2✔
400
            if (!partial_received) {
2✔
401
                partial_received = true;
1✔
402
                partial_prom.set_value(message);
1✔
403
            } else {
404
                full_prom.set_value(message);
1✔
405
            }
406
        });
2✔
407

408
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
409

410
    // Test 1: Send GPS_STATUS with partial array data (3 satellites)
411
    MavlinkDirect::MavlinkMessage partial_message;
1✔
412
    partial_message.message_name = "GPS_STATUS";
1✔
413
    partial_message.system_id = 1;
1✔
414
    partial_message.component_id = 1;
1✔
415
    partial_message.target_system_id = 0;
1✔
416
    partial_message.target_component_id = 0;
1✔
417
    partial_message.fields_json =
418
        R"({"satellites_visible":3,"satellite_prn":[1,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"satellite_used":[1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"satellite_elevation":[45,60,30,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"satellite_azimuth":[90,180,270,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"satellite_snr":[25,30,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]})";
1✔
419

420
    auto result1 = sender_mavlink_direct.send_message(partial_message);
1✔
421
    EXPECT_EQ(result1, MavlinkDirect::Result::Success);
1✔
422

423
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
1✔
424

425
    // Test 2: Send GPS_STATUS with full array data (20 satellites)
426
    MavlinkDirect::MavlinkMessage full_message;
1✔
427
    full_message.message_name = "GPS_STATUS";
1✔
428
    full_message.system_id = 1;
1✔
429
    full_message.component_id = 1;
1✔
430
    full_message.target_system_id = 0;
1✔
431
    full_message.target_component_id = 0;
1✔
432
    full_message.fields_json =
433
        R"({"satellites_visible":20,"satellite_prn":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20],"satellite_used":[1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0],"satellite_elevation":[45,60,30,75,20,85,50,40,65,35,25,55,70,15,80,10,90,5,45,60],"satellite_azimuth":[0,36,72,108,144,180,216,252,28,64,100,136,172,208,244,20,56,92,128,164],"satellite_snr":[25,30,15,35,20,40,28,22,32,18,26,31,16,38,24,19,33,21,29,27]})";
1✔
434

435
    auto result2 = sender_mavlink_direct.send_message(full_message);
1✔
436
    EXPECT_EQ(result2, MavlinkDirect::Result::Success);
1✔
437

438
    // Wait for both messages to be received
439
    ASSERT_EQ(partial_fut.wait_for(std::chrono::seconds(5)), std::future_status::ready);
1✔
440
    ASSERT_EQ(full_fut.wait_for(std::chrono::seconds(5)), std::future_status::ready);
1✔
441

442
    auto received_partial = partial_fut.get();
1✔
443
    auto received_full = full_fut.get();
1✔
444

445
    // Verify both messages were received and parsing worked
446
    EXPECT_EQ(received_partial.message_name, "GPS_STATUS");
1✔
447
    EXPECT_EQ(received_full.message_name, "GPS_STATUS");
1✔
448

449
    // Parse JSON to verify field values
450
    Json::Value partial_json, full_json;
1✔
451
    Json::Reader reader;
1✔
452

453
    ASSERT_TRUE(reader.parse(received_partial.fields_json, partial_json));
1✔
454
    ASSERT_TRUE(reader.parse(received_full.fields_json, full_json));
1✔
455

456
    // Verify scalar field
457
    EXPECT_EQ(partial_json["satellites_visible"].asUInt(), 3u);
1✔
458
    EXPECT_EQ(full_json["satellites_visible"].asUInt(), 20u);
1✔
459

460
    // Verify array fields are present and have correct type (arrays)
461
    EXPECT_TRUE(partial_json["satellite_prn"].isArray());
1✔
462
    EXPECT_TRUE(partial_json["satellite_used"].isArray());
1✔
463
    EXPECT_TRUE(partial_json["satellite_elevation"].isArray());
1✔
464
    EXPECT_TRUE(partial_json["satellite_azimuth"].isArray());
1✔
465
    EXPECT_TRUE(partial_json["satellite_snr"].isArray());
1✔
466

467
    EXPECT_TRUE(full_json["satellite_prn"].isArray());
1✔
468
    EXPECT_TRUE(full_json["satellite_used"].isArray());
1✔
469
    EXPECT_TRUE(full_json["satellite_elevation"].isArray());
1✔
470
    EXPECT_TRUE(full_json["satellite_azimuth"].isArray());
1✔
471
    EXPECT_TRUE(full_json["satellite_snr"].isArray());
1✔
472

473
    // Verify array lengths (should be 20 elements each)
474
    EXPECT_EQ(partial_json["satellite_prn"].size(), 20u);
1✔
475
    EXPECT_EQ(partial_json["satellite_used"].size(), 20u);
1✔
476
    EXPECT_EQ(partial_json["satellite_elevation"].size(), 20u);
1✔
477
    EXPECT_EQ(partial_json["satellite_azimuth"].size(), 20u);
1✔
478
    EXPECT_EQ(partial_json["satellite_snr"].size(), 20u);
1✔
479

480
    EXPECT_EQ(full_json["satellite_prn"].size(), 20u);
1✔
481
    EXPECT_EQ(full_json["satellite_used"].size(), 20u);
1✔
482
    EXPECT_EQ(full_json["satellite_elevation"].size(), 20u);
1✔
483
    EXPECT_EQ(full_json["satellite_azimuth"].size(), 20u);
1✔
484
    EXPECT_EQ(full_json["satellite_snr"].size(), 20u);
1✔
485

486
    // Verify specific array element values for partial message
487
    EXPECT_EQ(partial_json["satellite_prn"][0].asUInt(), 1u);
1✔
488
    EXPECT_EQ(partial_json["satellite_prn"][1].asUInt(), 2u);
1✔
489
    EXPECT_EQ(partial_json["satellite_prn"][2].asUInt(), 3u);
1✔
490
    EXPECT_EQ(partial_json["satellite_prn"][3].asUInt(), 0u); // Should be zero
1✔
491
    EXPECT_EQ(partial_json["satellite_prn"][19].asUInt(), 0u); // Last element should be zero
1✔
492

493
    EXPECT_EQ(partial_json["satellite_used"][0].asUInt(), 1u);
1✔
494
    EXPECT_EQ(partial_json["satellite_used"][1].asUInt(), 1u);
1✔
495
    EXPECT_EQ(partial_json["satellite_used"][2].asUInt(), 0u);
1✔
496
    EXPECT_EQ(partial_json["satellite_used"][3].asUInt(), 0u);
1✔
497

498
    EXPECT_EQ(partial_json["satellite_elevation"][0].asUInt(), 45u);
1✔
499
    EXPECT_EQ(partial_json["satellite_elevation"][1].asUInt(), 60u);
1✔
500
    EXPECT_EQ(partial_json["satellite_elevation"][2].asUInt(), 30u);
1✔
501
    EXPECT_EQ(partial_json["satellite_elevation"][3].asUInt(), 0u);
1✔
502

503
    // Verify specific array element values for full message
504
    EXPECT_EQ(full_json["satellite_prn"][0].asUInt(), 1u);
1✔
505
    EXPECT_EQ(full_json["satellite_prn"][9].asUInt(), 10u);
1✔
506
    EXPECT_EQ(full_json["satellite_prn"][19].asUInt(), 20u); // Last element
1✔
507

508
    EXPECT_EQ(full_json["satellite_used"][0].asUInt(), 1u);
1✔
509
    EXPECT_EQ(full_json["satellite_used"][5].asUInt(), 0u); // Some unused satellites
1✔
510
    EXPECT_EQ(full_json["satellite_used"][10].asUInt(), 1u);
1✔
511

512
    EXPECT_EQ(full_json["satellite_snr"][0].asUInt(), 25u);
1✔
513
    EXPECT_EQ(full_json["satellite_snr"][19].asUInt(), 27u); // Last element
1✔
514

515
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
516
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
517
}
1✔
518

519
TEST(SystemTest, MavlinkDirectLoadCustomXml)
4✔
520
{
521
    Mavsdk mavsdk_groundstation{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
522
    Mavsdk mavsdk_autopilot{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
523

524
    ASSERT_EQ(
1✔
525
        mavsdk_groundstation.add_any_connection("udpin://0.0.0.0:17005"),
526
        ConnectionResult::Success);
1✔
527
    ASSERT_EQ(
1✔
528
        mavsdk_autopilot.add_any_connection("udpout://127.0.0.1:17005"), ConnectionResult::Success);
1✔
529

530
    // Ground station discovers the autopilot system
531
    auto maybe_system = mavsdk_groundstation.first_autopilot(10.0);
1✔
532
    ASSERT_TRUE(maybe_system);
1✔
533
    auto system = maybe_system.value();
1✔
534
    ASSERT_TRUE(system->has_autopilot());
1✔
535

536
    // Wait for autopilot instance to discover the connection to the ground station
537
    while (mavsdk_autopilot.systems().size() == 0) {
2✔
538
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
539
    }
540

541
    auto groundstation_system = mavsdk_autopilot.systems().at(0);
1✔
542
    ASSERT_EQ(groundstation_system->component_ids()[0], 190);
2✔
543

544
    auto sender_mavlink_direct = MavlinkDirect{groundstation_system};
1✔
545
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
546

547
    // Define custom XML with a test message
548
    std::string custom_xml = R"(<?xml version="1.0"?>
1✔
549
<mavlink>
550
    <version>3</version>
551
    <dialect>0</dialect>
552
    <messages>
553
        <message id="420" name="CUSTOM_TEST_MESSAGE">
554
            <description>A test custom message for LoadCustomXml</description>
555
            <field type="uint32_t" name="test_value">Test value field</field>
556
            <field type="uint16_t" name="counter">Counter field</field>
557
            <field type="uint8_t" name="status">Status byte</field>
558
        </message>
559
    </messages>
560
</mavlink>)";
561

562
    // Load custom XML on both sender and receiver
563
    auto result1 = sender_mavlink_direct.load_custom_xml(custom_xml);
1✔
564
    EXPECT_EQ(result1, MavlinkDirect::Result::Success);
1✔
565

566
    auto result2 = receiver_mavlink_direct.load_custom_xml(custom_xml);
1✔
567
    EXPECT_EQ(result2, MavlinkDirect::Result::Success);
1✔
568

569
    auto prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
570
    auto fut = prom.get_future();
1✔
571

572
    // Subscribe to the custom message
573
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
574
        "CUSTOM_TEST_MESSAGE", [&prom](MavlinkDirect::MavlinkMessage message) {
2✔
575
            LogInfo() << "Received CUSTOM_TEST_MESSAGE: " << message.fields_json;
1✔
576
            prom.set_value(message);
1✔
577
        });
1✔
578

579
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
580

581
    // Send the custom message
582
    MavlinkDirect::MavlinkMessage custom_message;
1✔
583
    custom_message.message_name = "CUSTOM_TEST_MESSAGE";
1✔
584
    custom_message.system_id = 1;
1✔
585
    custom_message.component_id = 1;
1✔
586
    custom_message.target_system_id = 0;
1✔
587
    custom_message.target_component_id = 0;
1✔
588
    custom_message.fields_json = R"({"test_value":42,"counter":1337,"status":5})";
1✔
589

590
    auto send_result = sender_mavlink_direct.send_message(custom_message);
1✔
591
    EXPECT_EQ(send_result, MavlinkDirect::Result::Success);
1✔
592

593
    // Wait for message to be received
594
    ASSERT_EQ(fut.wait_for(std::chrono::seconds(5)), std::future_status::ready);
1✔
595

596
    auto received_message = fut.get();
1✔
597

598
    EXPECT_EQ(received_message.message_name, "CUSTOM_TEST_MESSAGE");
1✔
599

600
    // Parse JSON to verify field values
601
    Json::Value json;
1✔
602
    Json::Reader reader;
1✔
603
    ASSERT_TRUE(reader.parse(received_message.fields_json, json));
1✔
604

605
    // Verify custom message fields
606
    EXPECT_EQ(json["test_value"].asUInt(), 42u);
1✔
607
    EXPECT_EQ(json["counter"].asUInt(), 1337u);
1✔
608
    EXPECT_EQ(json["status"].asUInt(), 5u);
1✔
609

610
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
611
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
612
}
1✔
613

614
TEST(SystemTest, MavlinkDirectArdupilotmegaMessage)
4✔
615
{
616
    Mavsdk mavsdk_groundstation{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
617
    Mavsdk mavsdk_autopilot{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
618

619
    ASSERT_EQ(
1✔
620
        mavsdk_groundstation.add_any_connection("udpin://0.0.0.0:17000"),
621
        ConnectionResult::Success);
1✔
622
    ASSERT_EQ(
1✔
623
        mavsdk_autopilot.add_any_connection("udpout://127.0.0.1:17000"), ConnectionResult::Success);
1✔
624

625
    // Ground station discovers the autopilot system
626
    auto maybe_system = mavsdk_groundstation.first_autopilot(10.0);
1✔
627
    ASSERT_TRUE(maybe_system);
1✔
628
    auto system = maybe_system.value();
1✔
629
    ASSERT_TRUE(system->has_autopilot());
1✔
630

631
    // Wait for autopilot instance to discover the connection to the ground station
632
    LogInfo() << "Waiting for autopilot system to connect...";
1✔
633
    while (mavsdk_autopilot.systems().size() == 0) {
2✔
634
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
635
    }
636

637
    // Get the autopilot's view of the connected system
638
    auto groundstation_system = mavsdk_autopilot.systems().at(0);
1✔
639
    ASSERT_EQ(groundstation_system->component_ids()[0], 190);
2✔
640

641
    // Create separate MavlinkDirect instances for sender (autopilot) and receiver (ground station)
642
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
643
    auto sender_mavlink_direct = MavlinkDirect{groundstation_system};
1✔
644

645
    auto prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
646
    auto fut = prom.get_future();
1✔
647

648
    // Ground station subscribes to receive MEMINFO from autopilot (ArduPilot-specific message)
649
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
650
        "MEMINFO", [&prom](MavlinkDirect::MavlinkMessage message) {
2✔
651
            LogInfo() << "Received MEMINFO: " << message.fields_json;
1✔
652
            prom.set_value(message);
1✔
653
        });
1✔
654

655
    // Autopilot sends MEMINFO message (ID 152 from ardupilotmega.xml)
656
    MavlinkDirect::MavlinkMessage meminfo_msg;
1✔
657
    meminfo_msg.message_name = "MEMINFO";
1✔
658
    meminfo_msg.fields_json = R"({"brkval": 32768, "freemem": 8192})";
1✔
659

660
    auto result = sender_mavlink_direct.send_message(meminfo_msg);
1✔
661
    ASSERT_EQ(result, MavlinkDirect::Result::Success);
1✔
662

663
    // Wait for message to be received
664
    ASSERT_EQ(fut.wait_for(std::chrono::seconds(5)), std::future_status::ready);
1✔
665

666
    auto received_message = fut.get();
1✔
667

668
    // Verify the message was received
669
    EXPECT_EQ(received_message.message_name, "MEMINFO");
1✔
670

671
    // Parse JSON to verify field values
672
    Json::Value json;
1✔
673
    Json::Reader reader;
1✔
674
    ASSERT_TRUE(reader.parse(received_message.fields_json, json));
1✔
675

676
    // Verify MEMINFO message fields
677
    EXPECT_EQ(json["brkval"].asUInt(), 32768u); // Heap top
1✔
678
    EXPECT_EQ(json["freemem"].asUInt(), 8192u); // Free memory
1✔
679

680
    LogInfo() << "Successfully tested ArduPilot-specific MEMINFO message from ardupilotmega.xml";
1✔
681
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
682
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
683
}
1✔
684

685
TEST(SystemTest, MavlinkDirectNanInfinityJsonHandling)
4✔
686
{
687
    Mavsdk mavsdk_groundstation{Mavsdk::Configuration{ComponentType::GroundStation}};
1✔
688
    Mavsdk mavsdk_autopilot{Mavsdk::Configuration{ComponentType::Autopilot}};
1✔
689

690
    ASSERT_EQ(
1✔
691
        mavsdk_groundstation.add_any_connection("udpin://0.0.0.0:17006"),
692
        ConnectionResult::Success);
1✔
693
    ASSERT_EQ(
1✔
694
        mavsdk_autopilot.add_any_connection("udpout://127.0.0.1:17006"), ConnectionResult::Success);
1✔
695

696
    // Ground station discovers the autopilot system
697
    auto maybe_system = mavsdk_groundstation.first_autopilot(10.0);
1✔
698
    ASSERT_TRUE(maybe_system);
1✔
699
    auto system = maybe_system.value();
1✔
700
    ASSERT_TRUE(system->has_autopilot());
1✔
701

702
    // Wait for autopilot instance to discover the connection to the ground station
703
    while (mavsdk_autopilot.systems().size() == 0) {
2✔
704
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
705
    }
706

707
    auto groundstation_system = mavsdk_autopilot.systems().at(0);
1✔
708
    ASSERT_EQ(groundstation_system->component_ids()[0], 190);
2✔
709

710
    auto receiver_mavlink_direct = MavlinkDirect{system};
1✔
711
    auto sender_mavlink_direct = MavlinkDirect{groundstation_system};
1✔
712

713
    // Define custom XML with a test message containing float fields
714
    std::string custom_xml = R"(<?xml version="1.0"?>
1✔
715
<mavlink>
716
    <version>3</version>
717
    <dialect>0</dialect>
718
    <messages>
719
        <message id="421" name="FLOAT_TEST_MESSAGE">
720
            <description>A test message for NaN/infinity handling in JSON</description>
721
            <field type="float" name="normal_float">Normal float field</field>
722
            <field type="float" name="nan_float">NaN float field</field>
723
            <field type="float" name="pos_inf_float">Positive infinity float field</field>
724
            <field type="float" name="neg_inf_float">Negative infinity float field</field>
725
            <field type="double" name="normal_double">Normal double field</field>
726
            <field type="double" name="nan_double">NaN double field</field>
727
            <field type="float[4]" name="float_array">Array with some NaN/inf values</field>
728
        </message>
729
    </messages>
730
</mavlink>)";
731

732
    // Load custom XML on both sender and receiver
733
    auto result1 = sender_mavlink_direct.load_custom_xml(custom_xml);
1✔
734
    EXPECT_EQ(result1, MavlinkDirect::Result::Success);
1✔
735

736
    auto result2 = receiver_mavlink_direct.load_custom_xml(custom_xml);
1✔
737
    EXPECT_EQ(result2, MavlinkDirect::Result::Success);
1✔
738

739
    auto prom = std::promise<MavlinkDirect::MavlinkMessage>();
1✔
740
    auto fut = prom.get_future();
1✔
741

742
    // Subscribe to the custom message
743
    auto handle = receiver_mavlink_direct.subscribe_message(
1✔
744
        "FLOAT_TEST_MESSAGE", [&prom](MavlinkDirect::MavlinkMessage message) {
2✔
745
            LogInfo() << "Received FLOAT_TEST_MESSAGE: " << message.fields_json;
1✔
746
            prom.set_value(message);
1✔
747
        });
1✔
748

749
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
750

751
    // Create test message with NaN and infinity values
752
    // Note: We can't send these directly in JSON, so we'll simulate the scenario
753
    // where they would be received from a MAVLink message containing such values
754

755
    // For testing, we'll send a normal message and verify that the libmav receiver
756
    // handles NaN/inf correctly. The actual test will be done by checking that
757
    // valid JSON is produced even when NaN/inf values are present in libmav parsing.
758

759
    MavlinkDirect::MavlinkMessage test_message;
1✔
760
    test_message.message_name = "FLOAT_TEST_MESSAGE";
1✔
761
    test_message.system_id = 1;
1✔
762
    test_message.component_id = 1;
1✔
763
    test_message.target_system_id = 0;
1✔
764
    test_message.target_component_id = 0;
1✔
765
    test_message.fields_json =
766
        R"({"normal_float":3.14,"nan_float":null,"pos_inf_float":null,"neg_inf_float":null,"normal_double":2.718,"nan_double":null,"float_array":[1.0,null,null,4.0]})";
1✔
767

768
    auto send_result = sender_mavlink_direct.send_message(test_message);
1✔
769
    EXPECT_EQ(send_result, MavlinkDirect::Result::Success);
1✔
770

771
    // Wait for message to be received
772
    ASSERT_EQ(fut.wait_for(std::chrono::seconds(5)), std::future_status::ready);
1✔
773

774
    auto received_message = fut.get();
1✔
775

776
    EXPECT_EQ(received_message.message_name, "FLOAT_TEST_MESSAGE");
1✔
777

778
    // Parse JSON to verify it's valid JSON (most important test)
779
    Json::Value json;
1✔
780
    Json::Reader reader;
1✔
781
    ASSERT_TRUE(reader.parse(received_message.fields_json, json))
1✔
782
        << "JSON parsing failed, indicating invalid JSON was generated. "
×
783
        << "JSON content: " << received_message.fields_json;
1✔
784

785
    // Verify all expected fields are present
786
    EXPECT_TRUE(json.isMember("normal_float"));
1✔
787
    EXPECT_TRUE(json.isMember("nan_float"));
1✔
788
    EXPECT_TRUE(json.isMember("pos_inf_float"));
1✔
789
    EXPECT_TRUE(json.isMember("neg_inf_float"));
1✔
790
    EXPECT_TRUE(json.isMember("normal_double"));
1✔
791
    EXPECT_TRUE(json.isMember("nan_double"));
1✔
792
    EXPECT_TRUE(json.isMember("float_array"));
1✔
793

794
    // Verify that normal values are preserved correctly
795
    EXPECT_TRUE(json["normal_float"].isNumeric());
1✔
796
    EXPECT_NEAR(json["normal_float"].asFloat(), 3.14f, 0.001f);
1✔
797
    EXPECT_TRUE(json["normal_double"].isNumeric());
1✔
798
    EXPECT_NEAR(json["normal_double"].asDouble(), 2.718, 0.001);
1✔
799

800
    // The key test: verify that null values in input JSON were converted to NaN
801
    // in the MAVLink message, then back to null in the output JSON
802
    EXPECT_TRUE(json["nan_float"].isNull())
1✔
803
        << "nan_float should be null, got: " << json["nan_float"];
1✔
804
    EXPECT_TRUE(json["pos_inf_float"].isNull())
1✔
805
        << "pos_inf_float should be null, got: " << json["pos_inf_float"];
1✔
806
    EXPECT_TRUE(json["neg_inf_float"].isNull())
1✔
807
        << "neg_inf_float should be null, got: " << json["neg_inf_float"];
1✔
808
    EXPECT_TRUE(json["nan_double"].isNull())
1✔
809
        << "nan_double should be null, got: " << json["nan_double"];
1✔
810

811
    // Verify array handling: normal values preserved, null values round-trip as null (via NaN)
812
    EXPECT_TRUE(json["float_array"].isArray());
1✔
813
    EXPECT_EQ(json["float_array"].size(), 4u);
1✔
814
    EXPECT_TRUE(json["float_array"][0].isNumeric());
1✔
815
    EXPECT_NEAR(json["float_array"][0].asFloat(), 1.0f, 0.001f);
1✔
816
    EXPECT_TRUE(json["float_array"][1].isNull())
1✔
817
        << "float_array[1] should be null (converted from NaN), got: " << json["float_array"][1];
1✔
818
    EXPECT_TRUE(json["float_array"][2].isNull())
1✔
819
        << "float_array[2] should be null (converted from NaN), got: " << json["float_array"][2];
1✔
820
    EXPECT_TRUE(json["float_array"][3].isNumeric());
1✔
821
    EXPECT_NEAR(json["float_array"][3].asFloat(), 4.0f, 0.001f);
1✔
822

823
    LogInfo() << "Successfully verified that float/double JSON handling produces valid JSON";
1✔
824
    receiver_mavlink_direct.unsubscribe_message(handle);
1✔
825
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
1✔
826
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc