STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_sensors_core.c
Go to the documentation of this file.
1/*
2 * sensors_core under a virtual clock: the test plays the RTOS task (wakes on
3 * data-ready edges or on the core's next deadline) against fake chips.
4 */
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#include "fake_baro.h"
10#include "fake_regs.h"
11#include "sensors_core.h"
12
13#define CHECK(c) \
14 do { \
15 if (!(c)) { \
16 fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, #c); \
17 exit(1); \
18 } \
19 } while (0)
20
21typedef struct {
22 unsigned n[3];
26 uint32_t last_t[3];
28} sink_t;
29
30static void record(void* ctx, uint8_t type, const void* data, uint8_t len) {
31 sink_t* s = ctx;
32 int i = type - REC_IMU;
33 if (i < 0 || i > 2) {
34 return;
35 }
36 uint32_t t;
37 memcpy(&t, data, sizeof(t));
38 if (s->n[i] && (int32_t)(t - s->last_t[i]) <= 0) {
39 s->non_monotonic++;
40 }
41 s->last_t[i] = t;
42 s->n[i]++;
43 if (type == REC_IMU && len == sizeof(rec_imu_t))
44 memcpy(&s->imu, data, len);
45 if (type == REC_HIGHG && len == sizeof(rec_highg_t))
46 memcpy(&s->highg, data, len);
47 if (type == REC_BARO && len == sizeof(rec_baro_t))
48 memcpy(&s->baro, data, len);
49}
50
51typedef struct {
58} rig_t;
59
60static void rig_init(rig_t* r, uint32_t start_us) {
61 memset(r, 0, sizeof(*r));
62 fake_regs_init(&r->imu, false);
63 r->imu.regs[0x0F] = 0x6B;
64 r->imu.self_clear_reg = 0x12;
65 r->imu.self_clear_bits = 0x01;
66 r->imu.clear_after_reads = 1;
67 r->imu.self_clear_rearm = 1;
68 static const uint8_t imu_out[14] = {0x00, 0x02, 1, 0, 2, 0, 3,
69 0, 0x00, 0xF8, 0, 0, 0, 0x08};
70 memcpy(&r->imu.regs[0x20], imu_out, sizeof(imu_out));
71 fake_regs_init(&r->highg, true);
72 r->highg.regs[0x0F] = 0x32;
73 r->highg.regs[0x29] = (uint8_t)-13;
74 r->highg.regs[0x2B] = 0;
75 r->highg.regs[0x2D] = 64;
76 memcpy(r->baro.prom, fake_baro_datasheet_prom, sizeof(r->baro.prom));
77 r->baro.d1 = 9085466;
78 r->baro.d2 = 8569150;
79 r->core.imu_bus = fake_regs_bus(&r->imu);
80 r->core.highg_bus = fake_regs_bus(&r->highg);
81 r->core.baro_bus = fake_baro_bus(&r->baro);
82 r->core.sink.record = record;
83 r->core.sink.ctx = &r->sink;
84 r->now = start_us;
85 r->imu_edges = r->highg_edges = 1;
86 sensors_core_init(&r->core, r->now);
87 r->next_imu = r->now + SENSORS_IMU_PERIOD_US;
88 r->next_highg = r->now + SENSORS_HIGHG_PERIOD_US;
89}
90
91#define kWakeLatencyUs 37U /* a real task runs a little after its IRQ */
92
93static int32_t diff(uint32_t a, uint32_t b) {
94 return (int32_t)(a - b);
95}
96
97/* Advance virtual time to `until`, as sensors_task does: sleep until the next
98 * data-ready edge or the core's next deadline, wake kWakeLatencyUs after an
99 * edge, and pass the edge times in. Time never runs backwards: an edge that
100 * fell while the task was busy is handled on the next wake, late. */
101static void run(rig_t* r, uint32_t until) {
102 while (diff(until, r->now) > 0) {
103 uint32_t wake = r->now + sensors_core_wait_us(&r->core, r->now);
104 if (wake == r->now) {
105 wake = r->now + 1; /* deadline due: step now, then move on */
106 }
107 if (r->imu_edges && diff(r->next_imu, wake) < 0)
108 wake = r->next_imu;
109 if (r->highg_edges && diff(r->next_highg, wake) < 0)
110 wake = r->next_highg;
111 if (diff(wake, until) > 0)
112 wake = until;
113 uint32_t ev = 0;
114 if (r->imu_edges && diff(r->next_imu, wake) <= 0) {
115 ev |= SENSORS_EV_IMU;
116 r->edge_imu = r->next_imu;
117 r->next_imu += SENSORS_IMU_PERIOD_US;
118 }
119 if (r->highg_edges && diff(r->next_highg, wake) <= 0) {
120 ev |= SENSORS_EV_HIGHG;
121 r->edge_highg = r->next_highg;
122 r->next_highg += SENSORS_HIGHG_PERIOD_US;
123 }
124 uint32_t now = ev ? wake + kWakeLatencyUs : wake;
125 if (diff(now, r->now) > 0)
126 r->now = now;
127 sensors_core_step(&r->core, ev, r->edge_imu, r->edge_highg, r->now);
128 }
129}
130
132 static rig_t r;
133 rig_init(&r, 1000);
134 CHECK(r.core.imu.ok && r.core.highg.ok && r.core.baro.ok);
135 run(&r, 1000 + 1000000); /* one second */
136 CHECK(r.sink.n[0] >= 832 && r.sink.n[0] <= 834); /* 833 Hz */
137 CHECK(r.sink.n[1] == 400); /* 400 Hz */
138 CHECK(r.sink.n[2] >= 49 && r.sink.n[2] <= 50); /* D1+D2 = 20 ms */
139 CHECK(r.core.imu.missed_drdy == 0 && r.core.highg.missed_drdy == 0);
140 CHECK(r.sink.non_monotonic == 0);
141 /* Values decode from the fake chips' registers. */
142 CHECK(r.sink.imu.temp == 0x0200);
143 CHECK(r.sink.imu.gyro[0] == 1 && r.sink.imu.gyro[2] == 3);
144 CHECK(r.sink.imu.accel[0] == -2048 && r.sink.imu.accel[2] == 2048);
145 CHECK(r.sink.highg.accel[0] == -13 && r.sink.highg.accel[2] == 64);
146 CHECK(r.sink.baro.temp_c100 == 2007 && r.sink.baro.pressure_pa == 100009);
147 /* IMU samples carry the data-ready edge time, not the read time. */
148 CHECK(r.sink.imu.t_us == r.edge_imu);
149 CHECK(r.sink.highg.t_us == r.edge_highg);
150 CHECK(r.now != r.edge_imu); /* the task ran after the edge */
151}
152
154 static rig_t r;
155 rig_init(&r, 0);
156 r.highg_edges = 0; /* DRDY level stuck high: no more rising edges */
157 run(&r, 1000000);
158 /* one read per 2 periods (5 ms) instead of none */
159 CHECK(r.sink.n[1] >= 199 && r.sink.n[1] <= 200);
160 CHECK(r.core.highg.missed_drdy == r.sink.n[1]);
161 CHECK(r.sink.n[0] >= 832); /* the IMU is unaffected */
162}
163
165 static rig_t r;
166 rig_init(&r, 0);
167 run(&r, 100000);
168 unsigned before = r.sink.n[0];
169 r.imu.fail_after = (int)r.imu.transfers; /* every IMU transfer fails */
170 run(&r, 200000);
171 CHECK(r.sink.n[0] == before);
172 CHECK(!r.core.imu.ok);
173 CHECK(r.core.imu.errors == SENSORS_MAX_ERRORS);
174 CHECK(r.core.imu.last_error == DEV_ERR_BUS);
175 CHECK(r.sink.n[1] > 0 && r.core.highg.ok); /* others keep going */
176 r.imu.fail_after = -1; /* the chip comes back */
177 r.imu.self_clear_reg = 0x12;
178 r.imu.clear_after_reads = 1;
179 run(&r, 200000 + SENSORS_RETRY_US + 100000);
180 CHECK(r.core.imu.ok);
181 CHECK(r.core.imu.inits == 2);
182 CHECK(r.sink.n[0] > before + 80); /* sampling again */
183}
184
186 static rig_t r;
187 memset(&r, 0, sizeof(r));
188 rig_init(&r, 0);
189 r.highg.regs[0x0F] = 0x00; /* now answers the wrong ID */
190 r.core.highg.ok = false;
191 r.core.highg.retry_at_us = 0;
192 run(&r, 3 * SENSORS_RETRY_US + 1);
193 CHECK(!r.core.highg.ok);
194 CHECK(r.core.highg.inits >= 4);
195 CHECK(r.core.highg.last_error == DEV_ERR_ID);
196}
197
199 static rig_t r;
200 rig_init(&r, 0);
201 r.baro.fail = 1;
202 run(&r, 200000);
203 CHECK(!r.core.baro.ok);
204 CHECK(r.core.baro.last_error == DEV_ERR_BUS);
205}
206
207/* Idle: both accelerometers in power-down by their datasheets' registers,
208 * then a fresh init on Arm restores the full rates. */
210 static rig_t r;
211 rig_init(&r, 1000);
212 run(&r, 101000);
213 sensors_core_stop(&r.core);
214 CHECK(r.imu.regs[0x10] == 0x00 && r.imu.regs[0x11] == 0x00); /* ODR 0 */
215 CHECK(r.highg.regs[0x20] == 0x07); /* CTRL_REG1 PM = 000 */
216 CHECK(!r.core.imu.ok && !r.core.highg.ok && !r.core.baro.ok);
217 CHECK(r.core.baro_phase == 0);
218 unsigned before = r.sink.n[0];
219 r.now += 5000000U; /* 5 s of Idle: the task does not step the core */
220 sensors_core_init(&r.core, r.now);
221 CHECK(r.imu.regs[0x10] == 0x74 && r.highg.regs[0x20] == 0x37);
222 r.next_imu = r.now + SENSORS_IMU_PERIOD_US;
223 r.next_highg = r.now + SENSORS_HIGHG_PERIOD_US;
224 uint32_t t = r.now;
225 run(&r, t + 1000000U);
226 CHECK(r.sink.n[0] - before >= 832 && r.sink.n[0] - before <= 834);
227}
228
230 static rig_t r;
231 rig_init(&r, 0);
233 run(&r, 50000);
235}
236
238 static rig_t r;
239 rig_init(&r, 0xFFFFFFFFU - 500000U); /* wraps half way through */
240 run(&r, 500000U);
241 CHECK(r.sink.n[0] >= 832 && r.sink.n[1] == 400);
242 /* Timestamps compared modulo 2^32 stay in order across the wrap. */
243 CHECK(r.sink.non_monotonic == 0);
244}
245
@ DEV_ERR_ID
Definition dev_bus.h:19
@ DEV_ERR_BUS
Definition dev_bus.h:18
const uint16_t fake_baro_datasheet_prom[8]
Definition fake_baro.c:5
dev_bus_t fake_baro_bus(fake_baro_t *f)
Definition fake_baro.c:41
dev_bus_t fake_regs_bus(fake_regs_t *f)
Definition fake_regs.c:59
void fake_regs_init(fake_regs_t *f, bool ms_bit)
Definition fake_regs.c:5
*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
*brief user defined string literal for JSON values *sa std::size_t n
Definition json.hpp:24474
Definition run.py:1
rec_baro_t
Definition records.h:51
rec_highg_t
Definition records.h:45
@ REC_HIGHG
Definition records.h:21
@ REC_BARO
Definition records.h:22
@ REC_IMU
Definition records.h:20
rec_imu_t
Definition records.h:40
uint32_t sensors_core_wait_us(const sensors_core_t *c, uint32_t now_us)
void sensors_core_init(sensors_core_t *c, uint32_t now_us)
static uint32_t until(uint32_t now, uint32_t t)
void sensors_core_stop(sensors_core_t *c)
void sensors_core_step(sensors_core_t *c, uint32_t events, uint32_t imu_edge_us, uint32_t highg_edge_us, uint32_t now_us)
#define SENSORS_RETRY_US
#define SENSORS_BARO_STEP_US
#define SENSORS_MAX_ERRORS
#define SENSORS_HIGHG_PERIOD_US
#define SENSORS_IMU_PERIOD_US
#define SENSORS_EV_HIGHG
#define SENSORS_EV_IMU
uint32_t edge_imu
uint32_t next_highg
fake_regs_t highg
fake_baro_t baro
sink_t sink
uint32_t now
gnss_core_t core
uint32_t next_imu
fake_regs_t imu
uint32_t edge_highg
unsigned n
rec_imu_t imu
rec_highg_t highg
uint32_t last_t[3]
rec_baro_t baro
int main()
static boot_report_t r
static void record(void *ctx, uint8_t type, const void *data, uint8_t len)
static int32_t diff(uint32_t a, uint32_t b)
static void test_dead_baro_bus_is_reported(void)
static void test_clock_wrap_is_harmless(void)
static void test_highg_without_edges_is_read_on_timeout(void)
#define kWakeLatencyUs
static void test_nominal_rates_values_and_timestamps(void)
static void test_stop_powers_down_and_init_restarts(void)
static void rig_init(rig_t *r, uint32_t start_us)
#define CHECK(c)
static void record(void *ctx, uint8_t type, const void *data, uint8_t len)
static void test_failing_imu_is_dropped_and_recovered(void)
static void test_wrong_chip_never_produces_and_keeps_retrying(void)
static void test_wait_never_overshoots_a_deadline(void)