1 /*
2 * Copyright (c) 2022 Trackunit Corporation
3 * Copyright (c) 2025 Croxel Inc.
4 * Copyright (c) 2025 CogniPilot Foundation
5 *
6 * SPDX-License-Identifier: Apache-2.0
7 */
8
9 #include <zephyr/ztest.h>
10 #include <zephyr/kernel.h>
11
12 #include <zephyr/modem/ubx.h>
13 #include <zephyr/modem/ubx/protocol.h>
14 #include <modem_backend_mock.h>
15
16 static struct modem_ubx cmd;
17
18 static uint32_t cmd_user_data = 0x145212;
19 static uint8_t cmd_receive_buf[128];
20
21 static uint8_t cmd_response[128];
22
23 static struct modem_backend_mock mock;
24 static uint8_t mock_rx_buf[128];
25 static uint8_t mock_tx_buf[128];
26 static struct modem_pipe *mock_pipe;
27
28 #define MODEM_UBX_UTEST_ON_NAK_RECEIVED_BIT (0)
29 #define MODEM_UBX_UTEST_ON_ACK_RECEIVED_BIT (1)
30
31 static atomic_t callback_called;
32
on_nak_received(struct modem_ubx * ubx,const struct ubx_frame * frame,size_t len,void * user_data)33 static void on_nak_received(struct modem_ubx *ubx, const struct ubx_frame *frame, size_t len,
34 void *user_data)
35 {
36 atomic_set_bit(&callback_called, MODEM_UBX_UTEST_ON_NAK_RECEIVED_BIT);
37 }
38
on_ack_received(struct modem_ubx * ubx,const struct ubx_frame * frame,size_t len,void * user_data)39 static void on_ack_received(struct modem_ubx *ubx, const struct ubx_frame *frame, size_t len,
40 void *user_data)
41 {
42 atomic_set_bit(&callback_called, MODEM_UBX_UTEST_ON_ACK_RECEIVED_BIT);
43 }
44
45 MODEM_UBX_MATCH_ARRAY_DEFINE(unsol_matches,
46 MODEM_UBX_MATCH_DEFINE(UBX_CLASS_ID_ACK, UBX_MSG_ID_ACK, on_ack_received),
47 MODEM_UBX_MATCH_DEFINE(UBX_CLASS_ID_ACK, UBX_MSG_ID_NAK, on_nak_received)
48 );
49
50 static struct ubx_frame test_req = UBX_FRAME_ACK_INITIALIZER(0x01, 0x02);
51
52 struct script_runner {
53 struct modem_ubx_script script;
54 struct {
55 bool done;
56 int ret;
57 } result;
58 };
59
60 static struct script_runner test_script_runner;
61
test_setup(void)62 static void *test_setup(void)
63 {
64 const struct modem_ubx_config cmd_config = {
65 .user_data = &cmd_user_data,
66 .receive_buf = cmd_receive_buf,
67 .receive_buf_size = ARRAY_SIZE(cmd_receive_buf),
68 .unsol_matches = {
69 .array = unsol_matches,
70 .size = ARRAY_SIZE(unsol_matches),
71 },
72 };
73
74 zassert(modem_ubx_init(&cmd, &cmd_config) == 0, "Failed to init modem CMD");
75
76 const struct modem_backend_mock_config mock_config = {
77 .rx_buf = mock_rx_buf,
78 .rx_buf_size = ARRAY_SIZE(mock_rx_buf),
79 .tx_buf = mock_tx_buf,
80 .tx_buf_size = ARRAY_SIZE(mock_tx_buf),
81 .limit = 128,
82 };
83
84 mock_pipe = modem_backend_mock_init(&mock, &mock_config);
85 zassert(modem_pipe_open(mock_pipe, K_SECONDS(10)) == 0, "Failed to open mock pipe");
86 zassert(modem_ubx_attach(&cmd, mock_pipe) == 0, "Failed to attach pipe mock to modem CMD");
87
88 return NULL;
89 }
90
restore_ubx_script(void)91 static inline void restore_ubx_script(void)
92 {
93 static const struct ubx_frame frame_restored = UBX_FRAME_ACK_INITIALIZER(0x01, 0x02);
94 const struct script_runner script_runner_restored = {
95 .script = {
96 .request = {
97 .buf = &test_req,
98 .len = UBX_FRAME_SZ(frame_restored.payload_size),
99 },
100 .match = MODEM_UBX_MATCH_DEFINE(UBX_CLASS_ID_ACK, UBX_MSG_ID_ACK, NULL),
101 .response = {
102 .buf = cmd_response,
103 .buf_len = sizeof(cmd_response),
104 },
105 .timeout = K_SECONDS(1),
106 },
107 };
108
109 test_script_runner = script_runner_restored;
110 test_req = frame_restored;
111 }
112
test_before(void * f)113 static void test_before(void *f)
114 {
115 atomic_set(&callback_called, 0);
116 modem_backend_mock_reset(&mock);
117 restore_ubx_script();
118 }
119
120 ZTEST_SUITE(modem_ubx, NULL, test_setup, test_before, NULL, NULL);
121
122 static K_THREAD_STACK_ARRAY_DEFINE(stacks, 3, 2048);
123 static struct k_thread threads[3];
124
script_runner_handler(void * val,void * unused1,void * unused2)125 static void script_runner_handler(void *val, void *unused1, void *unused2)
126 {
127 struct script_runner *runner = (struct script_runner *)val;
128
129 int ret = modem_ubx_run_script(&cmd, &runner->script);
130
131 runner->result.done = true;
132 runner->result.ret = ret;
133 }
134
script_runner_start(struct script_runner * runner,uint8_t idx)135 static inline void script_runner_start(struct script_runner *runner, uint8_t idx)
136 {
137 k_thread_create(&threads[idx],
138 stacks[idx],
139 K_THREAD_STACK_SIZEOF(stacks[idx]),
140 script_runner_handler,
141 runner, NULL, NULL,
142 K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1),
143 0,
144 K_NO_WAIT);
145
146 k_thread_start(&threads[idx]);
147 }
148
test_thread_yield(void)149 static inline void test_thread_yield(void)
150 {
151 /** Used instead of k_yield() since internals of modem pipe may rely on
152 * multiple thread interactions which may not be served by simply
153 * yielding.
154 */
155 k_sleep(K_MSEC(1));
156 }
157
ZTEST(modem_ubx,test_cmd_no_rsp_is_non_blocking)158 ZTEST(modem_ubx, test_cmd_no_rsp_is_non_blocking)
159 {
160 /** Keep in mind this only happens if there isn't an on-going transfer
161 * already. If that happens, it will wait until the other script
162 * finishes or this request times out. Check test-case:
163 * test_script_is_thread_safe for details.
164 */
165 uint8_t buf[256];
166 int len;
167
168 /* Setting filter class to 0 means no response is to be awaited */
169 test_script_runner.script.match.filter.class = 0;
170
171 script_runner_start(&test_script_runner, 0);
172 test_thread_yield();
173
174 len = modem_backend_mock_get(&mock, buf, sizeof(buf));
175
176 zassert_true(test_script_runner.result.done, "Script should be done");
177 zassert_ok(test_script_runner.result.ret, "%d", test_script_runner.result.ret);
178 zassert_equal(UBX_FRAME_SZ(test_req.payload_size), len, "expected: %d, got: %d",
179 UBX_FRAME_SZ(test_req.payload_size), len);
180 }
181
ZTEST(modem_ubx,test_cmd_rsp_retries_and_times_out)182 ZTEST(modem_ubx, test_cmd_rsp_retries_and_times_out)
183 {
184 uint8_t buf[512];
185
186 test_script_runner.script.timeout = K_SECONDS(3);
187 test_script_runner.script.retry_count = 2; /* 2 Retries -> 3 Tries */
188
189 script_runner_start(&test_script_runner, 0);
190 test_thread_yield();
191
192 zassert_false(test_script_runner.result.done, "Script should not be done");
193
194 for (size_t i = 0 ; i < (test_script_runner.script.retry_count + 1) ; i++) {
195
196 int len = modem_backend_mock_get(&mock, buf, sizeof(buf));
197
198 zassert_false(test_script_runner.result.done, "Script should not be done. "
199 "Iteration: %d", i);
200 zassert_equal(UBX_FRAME_SZ(test_req.payload_size), len,
201 "Payload Sent does not match. "
202 "Expected: %d, Received: %d, Iteration: %d",
203 UBX_FRAME_SZ(test_req.payload_size), len, i);
204
205 k_sleep(K_SECONDS(1));
206 }
207
208 zassert_true(test_script_runner.result.done, "Script should be done");
209 zassert_equal(test_script_runner.result.ret, -EAGAIN, "Script should time out");
210 }
211
ZTEST(modem_ubx,test_cmd_rsp_blocks_and_receives_rsp)212 ZTEST(modem_ubx, test_cmd_rsp_blocks_and_receives_rsp)
213 {
214 static struct ubx_frame test_rsp = UBX_FRAME_ACK_INITIALIZER(0x02, 0x03);
215
216 script_runner_start(&test_script_runner, 0);
217 test_thread_yield();
218
219 zassert_false(test_script_runner.result.done, "Script should not be done");
220
221 modem_backend_mock_put(&mock, (uint8_t *)&test_rsp, UBX_FRAME_SZ(test_rsp.payload_size));
222 test_thread_yield();
223
224 zassert_true(test_script_runner.result.done, "Script should be done");
225 zassert_ok(test_script_runner.result.ret);
226 zassert_equal(UBX_FRAME_SZ(test_rsp.payload_size),
227 test_script_runner.script.response.received_len,
228 "expected: %d, got: %d",
229 UBX_FRAME_SZ(test_rsp.payload_size),
230 test_script_runner.script.response.received_len);
231 }
232
ZTEST(modem_ubx,test_script_is_thread_safe)233 ZTEST(modem_ubx, test_script_is_thread_safe)
234 {
235 static struct ubx_frame test_rsp = UBX_FRAME_ACK_INITIALIZER(0x02, 0x03);
236 struct script_runner script_runner_1 = {
237 .script = {
238 .request = {
239 .buf = &test_req,
240 .len = UBX_FRAME_SZ(test_req.payload_size),
241 },
242 .match = MODEM_UBX_MATCH_DEFINE(UBX_CLASS_ID_ACK, UBX_MSG_ID_ACK, NULL),
243 .response = {
244 .buf = cmd_response,
245 .buf_len = sizeof(cmd_response),
246 },
247 .timeout = K_SECONDS(1),
248 },
249 };
250 struct script_runner script_runner_2 = {
251 .script = {
252 .request = {
253 .buf = &test_req,
254 .len = UBX_FRAME_SZ(test_req.payload_size),
255 },
256 .match = MODEM_UBX_MATCH_DEFINE(UBX_CLASS_ID_ACK, UBX_MSG_ID_ACK, NULL),
257 .response = {
258 .buf = cmd_response,
259 .buf_len = sizeof(cmd_response),
260 },
261 .timeout = K_SECONDS(1),
262 },
263 };
264
265 script_runner_start(&script_runner_1, 0);
266 script_runner_start(&script_runner_2, 1);
267 test_thread_yield();
268
269 zassert_false(script_runner_1.result.done);
270 zassert_false(script_runner_2.result.done);
271
272 modem_backend_mock_put(&mock, (uint8_t *)&test_rsp, UBX_FRAME_SZ(test_rsp.payload_size));
273 test_thread_yield();
274
275 zassert_true(script_runner_1.result.done);
276 zassert_ok(script_runner_1.result.ret);
277 zassert_false(script_runner_2.result.done);
278
279 modem_backend_mock_put(&mock, (uint8_t *)&test_rsp, UBX_FRAME_SZ(test_rsp.payload_size));
280 test_thread_yield();
281
282 zassert_true(script_runner_2.result.done);
283 zassert_ok(script_runner_2.result.ret);
284 }
285
ZTEST(modem_ubx,test_rsp_filters_out_bytes_before_payload)286 ZTEST(modem_ubx, test_rsp_filters_out_bytes_before_payload)
287 {
288 static struct ubx_frame test_rsp = UBX_FRAME_ACK_INITIALIZER(0x02, 0x03);
289
290 /** Create a buf that contains an "AT command" followed by the UBX frame
291 * we're expecting. This should be handled by the modem_ubx.
292 */
293 char atcmd[] = "Here's an AT command: AT\r\nOK.";
294 uint8_t buf[256];
295 size_t buf_len = 0;
296
297 memcpy(buf, atcmd, sizeof(atcmd));
298 buf_len += sizeof(atcmd);
299 memcpy(buf + buf_len, &test_rsp, UBX_FRAME_SZ(test_rsp.payload_size));
300 buf_len += UBX_FRAME_SZ(test_rsp.payload_size);
301
302 script_runner_start(&test_script_runner, 0);
303 test_thread_yield();
304
305 zassert_false(test_script_runner.result.done, "Script should not be done");
306
307 modem_backend_mock_put(&mock, (uint8_t *)buf, buf_len);
308 test_thread_yield();
309
310 zassert_true(test_script_runner.result.done, "Script should be done");
311 zassert_ok(test_script_runner.result.ret);
312 zassert_equal(UBX_FRAME_SZ(test_rsp.payload_size),
313 test_script_runner.script.response.received_len,
314 "expected: %d, got: %d",
315 UBX_FRAME_SZ(test_rsp.payload_size),
316 test_script_runner.script.response.received_len);
317 zassert_mem_equal(&test_rsp,
318 test_script_runner.script.response.buf,
319 UBX_FRAME_SZ(test_rsp.payload_size));
320 }
321
ZTEST(modem_ubx,test_rsp_incomplete_packet_discarded)322 ZTEST(modem_ubx, test_rsp_incomplete_packet_discarded)
323 {
324 static struct ubx_frame test_rsp = UBX_FRAME_ACK_INITIALIZER(0x02, 0x03);
325
326 uint8_t buf[256];
327 size_t buf_len = 0;
328
329 memcpy(buf, &test_rsp, UBX_FRAME_SZ(test_rsp.payload_size) - 5);
330 buf_len += UBX_FRAME_SZ(test_rsp.payload_size) - 5;
331
332 script_runner_start(&test_script_runner, 0);
333 test_thread_yield();
334
335 zassert_false(test_script_runner.result.done, "Script should not be done");
336
337 modem_backend_mock_put(&mock, (uint8_t *)buf, buf_len);
338 test_thread_yield();
339
340 zassert_false(test_script_runner.result.done, "Script should not be done");
341
342 k_sleep(K_SECONDS(1));
343
344 zassert_true(test_script_runner.result.done, "Script should be done");
345 zassert_equal(-EAGAIN, test_script_runner.result.ret);
346 }
347
ZTEST(modem_ubx,test_rsp_discards_invalid_len)348 ZTEST(modem_ubx, test_rsp_discards_invalid_len)
349 {
350 static struct ubx_frame test_rsp = UBX_FRAME_ACK_INITIALIZER(0x02, 0x03);
351
352 /** Invalidate checksum */
353 size_t frame_size = UBX_FRAME_SZ(test_rsp.payload_size);
354
355 test_rsp.payload_size = 0xFFFF;
356
357 script_runner_start(&test_script_runner, 0);
358 test_thread_yield();
359
360 zassert_false(test_script_runner.result.done, "Script should not be done");
361
362 modem_backend_mock_put(&mock, (uint8_t *)&test_rsp, frame_size);
363 test_thread_yield();
364
365 zassert_false(test_script_runner.result.done, "Script should not be done");
366
367 k_sleep(K_SECONDS(1));
368
369 zassert_true(test_script_runner.result.done, "Script should be done");
370 zassert_equal(-EAGAIN, test_script_runner.result.ret);
371 }
372
ZTEST(modem_ubx,test_rsp_discards_invalid_checksum)373 ZTEST(modem_ubx, test_rsp_discards_invalid_checksum)
374 {
375 static struct ubx_frame test_rsp = UBX_FRAME_ACK_INITIALIZER(0x02, 0x03);
376
377 /** Invalidate checksum */
378 test_rsp.payload_and_checksum[test_rsp.payload_size] = 0xDE;
379 test_rsp.payload_and_checksum[test_rsp.payload_size + 1] = 0xAD;
380
381 script_runner_start(&test_script_runner, 0);
382 test_thread_yield();
383
384 zassert_false(test_script_runner.result.done, "Script should not be done");
385
386 modem_backend_mock_put(&mock, (uint8_t *)&test_rsp, UBX_FRAME_SZ(test_rsp.payload_size));
387 test_thread_yield();
388
389 zassert_false(test_script_runner.result.done, "Script should not be done");
390
391 k_sleep(K_SECONDS(1));
392
393 zassert_true(test_script_runner.result.done, "Script should be done");
394 zassert_equal(-EAGAIN, test_script_runner.result.ret);
395 }
396
ZTEST(modem_ubx,test_rsp_split_in_two_events)397 ZTEST(modem_ubx, test_rsp_split_in_two_events)
398 {
399 static struct ubx_frame test_rsp = UBX_FRAME_ACK_INITIALIZER(0x02, 0x03);
400 uint8_t *data_ptr = (uint8_t *)&test_rsp;
401
402 script_runner_start(&test_script_runner, 0);
403 test_thread_yield();
404
405 zassert_false(test_script_runner.result.done, "Script should not be done");
406
407 /** The first portion of the packet. At this point the data should not be discarded,
408 * understanding there's more data to come.
409 */
410 modem_backend_mock_put(&mock, data_ptr, UBX_FRAME_SZ(test_rsp.payload_size) - 5);
411 test_thread_yield();
412
413 zassert_false(test_script_runner.result.done, "Script should not be done");
414
415 /** The other portion of the packet. This should complete the packet reception */
416 modem_backend_mock_put(&mock, &data_ptr[UBX_FRAME_SZ(test_rsp.payload_size) - 5], 5);
417 test_thread_yield();
418
419 zassert_true(test_script_runner.result.done, "Script should be done");
420 zassert_ok(test_script_runner.result.ret);
421 zassert_equal(UBX_FRAME_SZ(test_rsp.payload_size),
422 test_script_runner.script.response.received_len,
423 "expected: %d, got: %d",
424 UBX_FRAME_SZ(test_rsp.payload_size),
425 test_script_runner.script.response.received_len);
426 }
427
ZTEST(modem_ubx,test_rsp_filters_out_non_matches)428 ZTEST(modem_ubx, test_rsp_filters_out_non_matches)
429 {
430 static struct ubx_frame test_rsp_non_match = UBX_FRAME_NAK_INITIALIZER(0x02, 0x03);
431 static struct ubx_frame test_rsp_match = UBX_FRAME_ACK_INITIALIZER(0x02, 0x03);
432
433 uint8_t buf[256];
434 size_t buf_len = 0;
435
436 /** We're passing a valid packet, but not what we're expecing. We
437 * should not get an event out of this one.
438 */
439 memcpy(buf, &test_rsp_non_match, UBX_FRAME_SZ(test_rsp_non_match.payload_size));
440 buf_len += UBX_FRAME_SZ(test_rsp_non_match.payload_size);
441
442 script_runner_start(&test_script_runner, 0);
443 test_thread_yield();
444
445 zassert_false(test_script_runner.result.done, "Script should not be done");
446
447 modem_backend_mock_put(&mock, (uint8_t *)buf, buf_len);
448 test_thread_yield();
449
450 zassert_false(test_script_runner.result.done, "Script should not be done");
451
452 /** Now we're passing two valid packets, on the same event: one which
453 * does not match, one which matches. We should get the latter.
454 */
455 memcpy(buf, &test_rsp_match, UBX_FRAME_SZ(test_rsp_match.payload_size));
456 buf_len += UBX_FRAME_SZ(test_rsp_match.payload_size);
457
458 modem_backend_mock_put(&mock, (uint8_t *)buf, buf_len);
459 test_thread_yield();
460
461 zassert_true(test_script_runner.result.done, "Script should be done");
462 zassert_ok(test_script_runner.result.ret);
463 zassert_equal(UBX_FRAME_SZ(test_rsp_match.payload_size),
464 test_script_runner.script.response.received_len,
465 "expected: %d, got: %d",
466 UBX_FRAME_SZ(test_rsp_match.payload_size),
467 test_script_runner.script.response.received_len);
468 zassert_mem_equal(&test_rsp_match,
469 test_script_runner.script.response.buf,
470 UBX_FRAME_SZ(test_rsp_match.payload_size));
471 }
472
ZTEST(modem_ubx,test_rsp_match_with_payload)473 ZTEST(modem_ubx, test_rsp_match_with_payload)
474 {
475 static struct ubx_frame test_rsp_non_match = UBX_FRAME_ACK_INITIALIZER(0x02, 0x03);
476 static struct ubx_frame test_rsp_match = UBX_FRAME_ACK_INITIALIZER(0x03, 0x04);
477
478 test_script_runner.script.match.filter.payload.buf = test_rsp_match.payload_and_checksum;
479 test_script_runner.script.match.filter.payload.len = test_rsp_match.payload_size;
480
481 uint8_t buf[256];
482 size_t buf_len = 0;
483
484 /** We're passing a valid packet, but not what we're expecing. We
485 * should not get an event out of this one.
486 */
487 memcpy(buf, &test_rsp_non_match, UBX_FRAME_SZ(test_rsp_non_match.payload_size));
488 buf_len += UBX_FRAME_SZ(test_rsp_non_match.payload_size);
489
490 script_runner_start(&test_script_runner, 0);
491 test_thread_yield();
492
493 zassert_false(test_script_runner.result.done, "Script should not be done");
494
495 modem_backend_mock_put(&mock, (uint8_t *)buf, buf_len);
496 test_thread_yield();
497
498 zassert_false(test_script_runner.result.done, "Script should not be done");
499
500 /** Now we're passing two valid packets, on the same event: one which
501 * does not match, one which matches. We should get the latter.
502 */
503 memcpy(buf, &test_rsp_match, UBX_FRAME_SZ(test_rsp_match.payload_size));
504 buf_len += UBX_FRAME_SZ(test_rsp_match.payload_size);
505
506 modem_backend_mock_put(&mock, (uint8_t *)buf, buf_len);
507 test_thread_yield();
508
509 zassert_true(test_script_runner.result.done, "Script should be done");
510 zassert_ok(test_script_runner.result.ret);
511 zassert_equal(UBX_FRAME_SZ(test_rsp_match.payload_size),
512 test_script_runner.script.response.received_len,
513 "expected: %d, got: %d",
514 UBX_FRAME_SZ(test_rsp_match.payload_size),
515 test_script_runner.script.response.received_len);
516 zassert_mem_equal(&test_rsp_match,
517 test_script_runner.script.response.buf,
518 UBX_FRAME_SZ(test_rsp_match.payload_size));
519 }
520
ZTEST(modem_ubx,test_unsol_matches_trigger_cb)521 ZTEST(modem_ubx, test_unsol_matches_trigger_cb)
522 {
523 static struct ubx_frame ack_frame = UBX_FRAME_ACK_INITIALIZER(0x01, 0x02);
524 static struct ubx_frame nak_frame = UBX_FRAME_NAK_INITIALIZER(0x01, 0x02);
525
526 zassert_false(atomic_test_bit(&callback_called, MODEM_UBX_UTEST_ON_ACK_RECEIVED_BIT));
527 zassert_false(atomic_test_bit(&callback_called, MODEM_UBX_UTEST_ON_NAK_RECEIVED_BIT));
528
529 modem_backend_mock_put(&mock,
530 (const uint8_t *)&ack_frame,
531 UBX_FRAME_SZ(ack_frame.payload_size));
532 test_thread_yield();
533
534 zassert_true(atomic_test_bit(&callback_called, MODEM_UBX_UTEST_ON_ACK_RECEIVED_BIT));
535 zassert_false(atomic_test_bit(&callback_called, MODEM_UBX_UTEST_ON_NAK_RECEIVED_BIT));
536
537 modem_backend_mock_put(&mock,
538 (const uint8_t *)&nak_frame,
539 UBX_FRAME_SZ(nak_frame.payload_size));
540 test_thread_yield();
541
542 zassert_true(atomic_test_bit(&callback_called, MODEM_UBX_UTEST_ON_NAK_RECEIVED_BIT));
543 }
544
ZTEST(modem_ubx,test_ubx_frame_encode_matches_compile_time_macro)545 ZTEST(modem_ubx, test_ubx_frame_encode_matches_compile_time_macro)
546 {
547 static struct ubx_frame ack_frame = UBX_FRAME_ACK_INITIALIZER(0x01, 0x02);
548
549 uint8_t buf[256];
550 struct ubx_ack ack = {
551 .class = 0x01,
552 .id = 0x02,
553 };
554
555 int len = ubx_frame_encode(UBX_CLASS_ID_ACK, UBX_MSG_ID_ACK,
556 (const uint8_t *)&ack, sizeof(ack),
557 buf, sizeof(buf));
558 zassert_equal(len, UBX_FRAME_SZ(sizeof(ack)), "Expected: %d, got: %d",
559 UBX_FRAME_SZ(sizeof(ack)), len);
560 zassert_mem_equal(buf, &ack_frame, UBX_FRAME_SZ(sizeof(ack)));
561 }
562