STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
bench_task.c
Go to the documentation of this file.
1#include "bench_task.h"
2
3#ifdef STAR_BENCH
4#include <stdarg.h>
5#include <stdio.h>
6
7#include "board.h"
8#include "board_time.h"
9#include "board_uart9.h"
10#include "cmsis_os2.h"
11#include "emmc_stm32.h"
12#include "gnss_task.h"
13#include "mode.h"
14#include "radio_task.h"
15#include "rtt_log.h"
16#include "sensors_task.h"
17
18/*
19 * Bench build only (STAR_BENCH): five times a second, one line per sensor
20 * to the RTT terminal (scripts/serial.sh). Integer math only: newlib-nano's
21 * printf has no %f. Units: ISM330DHCX accel 0.488 mg/LSB, gyro 140 mdps/LSB,
22 * temp 25 + raw/256 degC; H3LIS100DL 780 mg/digit; MS5611 compensated.
23 */
24
25/* 1: print only the GPS, once a second (antenna check). 0: every sensor,
26 * the GPS and the eMMC, five times a second, plus the 10 s ARMED eMMC test. */
27#ifndef BENCH_GPS_ONLY
28#define BENCH_GPS_ONLY 1
29#endif
30
31/* Radio link test between two boards (env:bench_rx / env:bench_tx):
32 * 1 = receive and report every frame, 2 = send one frame a second. 0 = off.
33 */
34#ifndef BENCH_RADIO
35#define BENCH_RADIO 0
36#endif
37#ifndef BENCH_TX_PERIOD_MS
38#define BENCH_TX_PERIOD_MS 1000U
39#endif
40
41static void say(const char* fmt, ...) __attribute__((format(printf, 1, 2)));
42
43static void say(const char* fmt, ...) {
44 char line[256];
45 va_list ap;
46 va_start(ap, fmt);
47 int n = vsnprintf(line, sizeof line, fmt, ap);
48 va_end(ap);
49 if (n > 0) {
50 (void)rtt_log_write(line, n < (int)sizeof line ? (uint32_t)n
51 : sizeof line - 1U);
52 }
53}
54
55static const char* disk_name(emmc_disk_state_t d) {
56 switch (d) {
58 return "unmounted";
60 return "MOUNTED";
61 case EMMC_DISK_FULL:
62 return "FULL";
63 default:
64 return "FAULT";
65 }
66}
67
68/* eMMC: card identity once it is up, then the log's progress. */
69static void emmc_line(const char* phase) {
70 const emmc_log_t* l = emmc_stm32_log();
71 uint32_t issues = 0;
72 const emmc_card_info_t* c = emmc_stm32_card(&issues);
73 say(" eMMC %-9s %-9s records %lu chunks written %lu "
74 "write errors %lu dropped %lu backlog %u\r\n",
75 phase, disk_name(l->disk), (unsigned long)l->stats.records,
76 (unsigned long)l->stats.chunks_written,
77 (unsigned long)l->stats.write_errors, (unsigned long)l->stats.dropped,
79 static bool card_said;
80 if (c && !card_said) {
81 card_said = true;
82 say(" eMMC card: maker 0x%02x part %s rev %u %lu MB "
83 "serial %08lx life used A/B %u/%u (1 = 0-10%%) pre-EOL %u "
84 "issues 0x%02lx session %lu at LBA %lu\r\n",
85 c->mid, c->pnm, c->ext_csd_rev,
86 (unsigned long)(c->sec_count / 2048U), (unsigned long)c->psn,
87 c->life_a, c->life_b, c->pre_eol, (unsigned long)issues,
88 (unsigned long)l->base_session, (unsigned long)l->head);
89 }
90}
91
92static long mg(long raw, long ug_per_lsb) {
93 return raw * ug_per_lsb / 1000L;
94}
95
96#if BENCH_GPS_ONLY
97/* 1e-7 degrees as signed decimal degrees, e.g. "37.8715926". */
98static void deg_e7(char* out, size_t n, int32_t v) {
99 long a = v < 0 ? -(long)v : (long)v;
100 snprintf(out, n, "%s%ld.%07ld", v < 0 ? "-" : "", a / 10000000L,
101 a % 10000000L);
102}
103
104static const char* fix_name(uint8_t t) {
105 switch (t) {
106 case 0:
107 return "none";
108 case 1:
109 return "DR";
110 case 2:
111 return "2D";
112 case 3:
113 return "3D";
114 case 4:
115 return "GNSS+DR";
116 case 5:
117 return "time";
118 default:
119 return "?";
120 }
121}
122
123/* GPS only, once a second: for checking the antenna. No other lines and no
124 * ARMED eMMC test; the board stays in SELF_TEST. */
125static void gps_only_loop(void) {
126 uint32_t last_fix = 0;
127 uint32_t last_ms = osKernelGetTickCount();
128 say("\r\nSTAR flight computer bench build: GPS only (BENCH_GPS_ONLY)\r\n");
129 gnss_task_poll_status(true); /* MON-RF + NAV-SAT once a second */
130 for (;;) {
131 osDelay(1000);
132 const gnss_core_t* g = gnss_task_core();
133 const rec_gnss_t* f = &g->latest;
134 uint32_t now = osKernelGetTickCount(), dt = now - last_ms;
135 last_ms = now;
136 uint32_t hz = dt ? (g->fixes - last_fix) * 1000U / dt : 0U;
137 last_fix = g->fixes;
138 const char* st = g->state == GNSS_RUN ? "run"
139 : g->state == GNSS_CONFIG ? "config"
140 : "detect";
141 char lat[16], lon[16];
142 deg_e7(lat, sizeof lat, f->lat_e7);
143 deg_e7(lon, sizeof lon, f->lon_e7);
144 /* listen: the baud the task is on now; heard: the last baud a valid
145 * frame came in at (0 = none yet). rx/err/ovf: UART9 since boot. */
146 say("t=%lus GPS %-6s listen %lu heard %lu rx %lu err %lu ovf %lu "
147 "%2luHz fix %-7s sats %2u "
148 "lat %s lon %s alt %ld m hacc %lu m pdop %u.%02u\r\n",
149 (unsigned long)(now / 1000U), st, (unsigned long)g->baud,
150 (unsigned long)g->detected_baud,
151 (unsigned long)board_uart9_rx_bytes(),
152 (unsigned long)board_uart9_errors(),
153 (unsigned long)board_uart9_overflows(), (unsigned long)hz,
154 fix_name(f->fix_type), f->num_sv, lat, lon,
155 (long)(f->hmsl_mm / 1000), (unsigned long)(f->hacc_mm / 1000U),
156 f->pdop_e2 / 100U, f->pdop_e2 % 100U);
157 static const char* const ant[] = {"init", "unknown", "OK", "SHORT",
158 "OPEN"};
159 static const char* const pwr[] = {"off", "on", "unknown"};
160 static const char* const jam[] = {"unknown", "ok", "WARNING",
161 "CRITICAL"};
162 const ubx_rf_t* r = &g->rf;
163 const ubx_sats_t* v = &g->sats;
164 say(" antenna %s (power %s) jamming %s noise %u agc %u "
165 "| sats listed %u signal %u locked %u used %u best %u dB-Hz "
166 "(reports rf %lu sat %lu)\r\n",
167 r->ant_status < 5U ? ant[r->ant_status] : "?",
168 r->ant_power < 3U ? pwr[r->ant_power] : "?", jam[r->jamming & 3U],
169 r->noise, r->agc, v->listed, v->with_signal, v->locked, v->used,
170 v->best_cno, (unsigned long)g->rf_reports,
171 (unsigned long)g->sat_reports);
172 }
173}
174
175#endif /* BENCH_GPS_ONLY */
176
177#if BENCH_RADIO
178/* Half and quarter units to "-90.5" / "-5.25" without %f. */
179static void fixed(char* out, size_t n, long v, long den) {
180 long a = v < 0 ? -v : v;
181 long frac = (a % den) * (den == 2 ? 5 : 25);
182 snprintf(out, n, "%s%ld.%0*ld", v < 0 ? "-" : "", a / den, den == 2 ? 1 : 2,
183 frac);
184}
185
186static void radio_header(const char* role) {
189 say("\r\nSTAR flight computer bench build: radio link test, %s\r\n"
190 " LoRa 915 MHz, SF9, BW 125 kHz, CR 4/5, sync 0x1424"
191#ifdef STAR_RADIO_POWER_DBM
192 ", TX power %d dBm"
193#endif
194 "\r\n",
195 role
196#ifdef STAR_RADIO_POWER_DBM
197 ,
198 STAR_RADIO_POWER_DBM
199#endif
200 );
201}
202
203/* Wait for the radio task's start-up (configure()); report the failing
204 * step while it retries every 5 s. */
205static void wait_radio_ready(void) {
206 for (;;) {
209 if (st.setup_attempts > 0U && st.setup_step == 0U) {
210 say(" radio ready (start-up attempt %lu)\r\n",
211 (unsigned long)st.setup_attempts);
212 return;
213 }
214 if (st.setup_attempts > 0U) {
215 say(" radio NOT ready: start-up stopped at step %u (attempt %lu, "
216 "device errors 0x%04x); retrying\r\n",
217 st.setup_step, (unsigned long)st.setup_attempts,
218 st.device_errors);
219 }
220 osDelay(st.setup_attempts > 0U ? 5000U : 200U);
221 }
222}
223
224#if BENCH_RADIO == 1
225static void radio_rx_loop(void) {
226 radio_header("RX (this board listens)");
227 wait_radio_ready();
228 uint32_t seen = 0, got = 0, last_report = osKernelGetTickCount();
229 uint16_t first_seq = 0, last_seq = 0;
230 long rssi_sum = 0, snr_sum = 0;
231 int best_rssi = -1000, worst_rssi = 1000;
232 for (;;) {
233 osDelay(20);
234 radio_rx_t rx;
236 uint32_t now = osKernelGetTickCount();
237 if (rx.count != seen) {
238 uint32_t missed_here = rx.count - seen - 1U; /* not seen by us */
239 seen = rx.count;
240 if (got == 0U || (uint16_t)(rx.seq - last_seq) > 1000U) {
241 if (got != 0U) {
242 say(" -- sequence jumped %u -> %u: the TX board "
243 "restarted, counting again --\r\n",
244 last_seq, rx.seq);
245 }
246 first_seq = rx.seq;
247 got = 0;
248 rssi_sum = snr_sum = 0;
249 best_rssi = -1000;
250 worst_rssi = 1000;
251 }
252 got += 1U + missed_here;
253 last_seq = rx.seq;
254 uint32_t sent = (uint16_t)(rx.seq - first_seq) + 1U;
255 uint32_t lost = sent > got ? sent - got : 0U;
256 rssi_sum += rx.rssi_x2;
257 snr_sum += rx.snr_x4;
258 if (rx.rssi_x2 > best_rssi)
259 best_rssi = rx.rssi_x2;
260 if (rx.rssi_x2 < worst_rssi)
261 worst_rssi = rx.rssi_x2;
264 char rssi[12], snr[12];
265 fixed(rssi, sizeof rssi, rx.rssi_x2, 2);
266 fixed(snr, sizeof snr, rx.snr_x4, 4);
267 uint32_t counter = 0;
268 if (rx.payload_len >= 5U && rx.payload[0] == 'T') {
269 counter = (uint32_t)rx.payload[1] |
270 (uint32_t)rx.payload[2] << 8 |
271 (uint32_t)rx.payload[3] << 16 |
272 (uint32_t)rx.payload[4] << 24;
273 }
274 say("t=%lu.%03lus RX seq %5u type 0x%02x len %u RSSI %s dBm "
275 "SNR %s dB%s | got %lu of %lu lost %lu CRC errors %lu "
276 "bad frames %lu (TX count %lu)\r\n",
277 (unsigned long)(now / 1000U), (unsigned long)(now % 1000U),
278 rx.seq, rx.type, rx.payload_len, rssi, snr,
279 rx.quality_ok ? "" : " (no status)", (unsigned long)got,
280 (unsigned long)sent, (unsigned long)lost,
281 (unsigned long)st.crc_errors, (unsigned long)st.frames_dropped,
282 (unsigned long)counter);
283 last_report = now;
284 } else if (now - last_report >= 5000U) {
287 if (got) {
288 char avg_rssi[12], avg_snr[12], lo[12], hi[12];
289 fixed(avg_rssi, sizeof avg_rssi, rssi_sum / (long)seen, 2);
290 fixed(avg_snr, sizeof avg_snr, snr_sum / (long)seen, 4);
291 fixed(lo, sizeof lo, worst_rssi, 2);
292 fixed(hi, sizeof hi, best_rssi, 2);
293 say("t=%lus ... nothing for 5 s. So far: got %lu, avg RSSI %s "
294 "dBm (%s .. %s), avg SNR %s dB, CRC errors %lu\r\n",
295 (unsigned long)(now / 1000U), (unsigned long)got, avg_rssi,
296 lo, hi, avg_snr, (unsigned long)st.crc_errors);
297 } else {
298 say("t=%lus listening... no frames yet (CRC errors %lu, "
299 "bad frames %lu, radio errors %lu)\r\n",
300 (unsigned long)(now / 1000U), (unsigned long)st.crc_errors,
301 (unsigned long)st.frames_dropped,
302 (unsigned long)st.radio_errors);
303 }
304 last_report = now;
305 }
306 }
307}
308#endif /* BENCH_RADIO == 1 */
309
310#if BENCH_RADIO == 2
311static void radio_tx_loop(void) {
312 radio_header("TX (this board sends one frame a second)");
313 wait_radio_ready();
314 uint32_t counter = 0, next = osKernelGetTickCount();
315 for (;;) {
316 next += BENCH_TX_PERIOD_MS;
317 /* Heartbeat-sized test payload: 'T', counter (u32 LE), 2 spare. */
318 uint8_t p[7] = {'T',
319 (uint8_t)counter,
320 (uint8_t)(counter >> 8),
321 (uint8_t)(counter >> 16),
322 (uint8_t)(counter >> 24),
323 0,
324 0};
325 uint32_t t0 = board_now_ms();
327 radio_task_send_frame(0x01 /* Heartbeat */, p, sizeof p);
328 uint32_t took = board_now_ms() - t0;
331 static const char* const names[] = {"OK", "TIMEOUT (no TxDone)",
332 "NO RADIO", "BAD FRAME"};
333 uint32_t now = osKernelGetTickCount();
334 say("t=%lu.%03lus TX #%lu %s in %lu ms | sent ok %lu timeouts %lu"
335 "\r\n",
336 (unsigned long)(now / 1000U), (unsigned long)(now % 1000U),
337 (unsigned long)counter, r < 4 ? names[r] : "?", (unsigned long)took,
338 (unsigned long)st.tx_ok, (unsigned long)st.tx_timeouts);
339 counter++;
340 int32_t wait = (int32_t)(next - osKernelGetTickCount());
341 osDelay(wait > 0 ? (uint32_t)wait : 1U);
342 }
343}
344#endif /* BENCH_RADIO == 2 */
345#endif /* BENCH_RADIO */
346
347static void bench_task(void* argument) {
348 (void)argument;
349#if BENCH_RADIO == 1
350 radio_rx_loop();
351#elif BENCH_RADIO == 2
352 radio_tx_loop();
353#endif
354#if BENCH_GPS_ONLY
355 gps_only_loop();
356#endif
357 static sensors_core_t s; /* static: the snapshot is too big for a stack */
358 uint32_t last_imu = 0, last_highg = 0, last_baro = 0, last_fix = 0;
359 uint32_t last_ms = osKernelGetTickCount();
360 say("\r\nSTAR flight computer bench build: sensors + GPS live, no Idle, "
361 "no eMMC, no pyro\r\n");
362 /* One 10 s recording (ARMED: sensors + GPS to the eMMC, never pyro),
363 * starting at 5 s, then back to SELF_TEST. */
364 const uint32_t rec_start_ms = 5000U, rec_stop_ms = 15000U;
365 bool rec_started = false, rec_stopped = false;
366 for (;;) {
367 osDelay(200);
368 uint32_t up = osKernelGetTickCount();
369 if (!rec_started && up >= rec_start_ms) {
370 rec_started = true;
371 say("--- eMMC test: ARMED for 10 s, recording ---\r\n");
373 } else if (rec_started && !rec_stopped && up >= rec_stop_ms) {
374 rec_stopped = true;
376 sensors_task_set_active(true); /* SELF_TEST stops them: */
377 gnss_task_set_active(true); /* keep the bench stream going */
378 say("--- eMMC test: stopped, flushing ---\r\n");
379 }
380 emmc_line(!rec_started ? "waiting"
381 : rec_stopped ? "done"
382 : "RECORDING");
384 const gnss_core_t* g = gnss_task_core();
385 uint32_t now = osKernelGetTickCount(), dt = now - last_ms;
386 last_ms = now;
387 /* Rates from the sample counters over the last interval. */
388 uint32_t imu_hz = (s.imu.samples - last_imu) * 1000U / dt;
389 uint32_t hg_hz = (s.highg.samples - last_highg) * 1000U / dt;
390 uint32_t baro_hz = (s.baro.samples - last_baro) * 1000U / dt;
391 last_imu = s.imu.samples;
392 last_highg = s.highg.samples;
393 last_baro = s.baro.samples;
394
395 const rec_imu_t* i = &s.imu_latest;
396 long t100 = 2500L + (long)i->temp * 100L / 256L;
397 say("t=%lu.%01lus IMU %s %3luHz acc mg %6ld %6ld %6ld "
398 "gyro dps %5ld %5ld %5ld %ld.%02ldC\r\n",
399 (unsigned long)(now / 1000U), (unsigned long)(now % 1000U / 100U),
400 s.imu.ok ? "ok " : "ERR", (unsigned long)imu_hz,
401 mg(i->accel[0], 488), mg(i->accel[1], 488), mg(i->accel[2], 488),
402 (long)i->gyro[0] * 140L / 1000L, (long)i->gyro[1] * 140L / 1000L,
403 (long)i->gyro[2] * 140L / 1000L, t100 / 100L,
404 (t100 < 0 ? -t100 : t100) % 100L);
405
406 const rec_highg_t* h = &s.highg_latest;
407 say(" high-g %s %3luHz acc mg %6ld %6ld %6ld\r\n",
408 s.highg.ok ? "ok " : "ERR", (unsigned long)hg_hz,
409 (long)h->accel[0] * 780L, (long)h->accel[1] * 780L,
410 (long)h->accel[2] * 780L);
411
412 const rec_baro_t* b = &s.baro_latest;
413 long tc = b->temp_c100;
414 say(" baro %s %3luHz %ld.%02ld hPa %ld.%02ldC\r\n",
415 s.baro.ok ? "ok " : "ERR", (unsigned long)baro_hz,
416 (long)b->pressure_pa / 100L, (long)b->pressure_pa % 100L, tc / 100L,
417 (tc < 0 ? -tc : tc) % 100L);
418
419 const rec_gnss_t* f = &g->latest;
420 const char* st = g->state == GNSS_RUN ? "run"
421 : g->state == GNSS_CONFIG ? "config"
422 : "detect";
423 say(" GPS %-6s %lu baud fixes %lu (+%lu) fix %u sats %u"
424 " lat %ld lon %ld (1e-7 deg) alt %ld m\r\n",
425 st, (unsigned long)g->detected_baud, (unsigned long)g->fixes,
426 (unsigned long)(g->fixes - last_fix), f->fix_type, f->num_sv,
427 (long)f->lat_e7, (long)f->lon_e7, (long)(f->hmsl_mm / 1000));
428 last_fix = g->fixes;
429 }
430}
431
432bool bench_task_start(void) {
433 static const osThreadAttr_t attr = {
434 .name = "bench",
435 .stack_size = 2048,
436 .priority = osPriorityLow,
437 };
438 rtt_log_init();
439 return osThreadNew(bench_task, NULL, &attr) != NULL;
440}
441#endif /* STAR_BENCH */
@ FLIGHT_SELF_TEST
@ FLIGHT_ARMED
bool bench_task_start(void)
uint32_t board_now_ms(void)
Definition board.c:24
uint32_t board_uart9_overflows(void)
Definition board_uart9.c:70
uint32_t board_uart9_rx_bytes(void)
Definition board_uart9.c:67
uint32_t board_uart9_errors(void)
Definition board_uart9.c:73
osThreadId_t osThreadNew(osThreadFunc_t func, void *argument, const osThreadAttr_t *attr)
Create a thread and add it to Active Threads.
Definition cmsis_os2.c:577
uint32_t osKernelGetTickCount(void)
Get the RTOS kernel tick count.
Definition cmsis_os2.c:491
@ osPriorityLow
Priority: low.
Definition cmsis_os2.h:107
osStatus_t osDelay(uint32_t ticks)
Wait for Timeout (Time Delay).
Definition cmsis_os2.c:1094
static emmc_status_t format(emmc_log_t *log)
Definition emmc_log.c:324
uint8_t emmc_log_backlog(const emmc_log_t *log)
Definition emmc_log.c:252
emmc_disk_state_t
Definition emmc_log.h:92
@ EMMC_DISK_FULL
Definition emmc_log.h:95
@ EMMC_DISK_UNMOUNTED
Definition emmc_log.h:93
@ EMMC_DISK_MOUNTED
Definition emmc_log.h:94
const emmc_card_info_t * emmc_stm32_card(uint32_t *issues)
Definition emmc_stm32.c:250
emmc_log_t * emmc_stm32_log(void)
Definition emmc_stm32.c:246
int32_t p
Definition flight_e2e.c:78
@ GNSS_CONFIG
Definition gnss_core.h:48
@ GNSS_RUN
Definition gnss_core.h:48
void gnss_task_set_active(bool on)
Definition gnss_task.c:99
const gnss_core_t * gnss_task_core(void)
Definition gnss_task.c:110
void gnss_task_poll_status(bool on)
Definition gnss_task.c:106
#define NULL
Definition usbd_def.h:44
*brief create JSON pointer *sa reference_tokens const string_t const string_t & b
Definition json.hpp:13878
*brief create JSON pointer *sa reference_tokens const string_t & a
Definition json.hpp:13877
void app_set_state(flight_state_t to)
Definition mode.c:22
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:24474
float dt
Definition lqr_motor.py:39
dict[str, object] line(str eid, str a, str b, str from_port="", str to_port="")
void radio_task_status(radio_status_t *out)
void radio_task_last_rx(radio_rx_t *out)
radio_send_result_t radio_task_send_frame(uint8_t type, const uint8_t *payload, uint8_t len)
radio_send_result_t
Definition radio_task.h:25
rec_gnss_t
Definition records.h:63
rec_baro_t
Definition records.h:51
rec_highg_t
Definition records.h:45
rec_imu_t
Definition records.h:40
void rtt_log_init(void)
uint32_t rtt_log_write(const char *s, uint32_t n)
void sensors_task_snapshot(sensors_core_t *out)
void sensors_task_set_active(bool on)
uint32_t sec_count
Definition emmc_card.h:42
uint8_t ext_csd_rev
Definition emmc_card.h:41
uint8_t pre_eol
Definition emmc_card.h:44
uint32_t records
Definition emmc_log.h:100
uint32_t write_errors
Definition emmc_log.h:104
uint32_t chunks_written
Definition emmc_log.h:102
uint32_t dropped
Definition emmc_log.h:101
emmc_log_stats_t stats
Definition emmc_log.h:145
emmc_disk_state_t disk
Definition emmc_log.h:139
uint32_t base_session
Definition emmc_log.h:143
uint32_t head
Definition emmc_log.h:142
Attributes structure for thread.
Definition cmsis_os2.h:240
uint32_t count
Definition radio_task.h:46
uint8_t payload[16]
Definition radio_task.h:51
uint8_t payload_len
Definition radio_task.h:50
int16_t rssi_x2
Definition radio_task.h:53
bool quality_ok
Definition radio_task.h:52
int8_t snr_x4
Definition radio_task.h:54
uint16_t seq
Definition radio_task.h:49
uint8_t type
Definition radio_task.h:48
uint32_t tx_ok
Definition radio_task.h:39
uint8_t setup_step
Definition radio_task.h:35
uint32_t radio_errors
Definition radio_task.h:38
uint32_t tx_timeouts
Definition radio_task.h:39
uint16_t device_errors
Definition radio_task.h:36
uint32_t setup_attempts
Definition radio_task.h:37
uint32_t crc_errors
Definition radio_task.h:38
uint32_t frames_dropped
Definition radio_task.h:38
rec_highg_t highg_latest
sensors_device_t highg
sensors_device_t imu
rec_imu_t imu_latest
sensors_device_t baro
rec_baro_t baro_latest
Definition ubx.h:96
uint8_t used
Definition ubx.h:116
uint8_t best_cno
Definition ubx.h:117
uint8_t locked
Definition ubx.h:115
uint8_t with_signal
Definition ubx.h:114
uint8_t listed
Definition ubx.h:112
__attribute__((weak))
Definition syscalls.c:67
static ArmGate g(kMe)
static boot_report_t r