36#define FLIGHT_US 20000000U
37#define T_LAUNCH_US 2000000U
38#define T_BURNOUT_US 4000000U
39#define T_RECOVERY_US 10000000U
56 static const int16_t accel_z[] = {2049, 16393, 0, 2049};
57 uint32_t ms = t / 1000U;
58 out[0] = (int16_t)(ms % 256U);
59 out[1] = (int16_t)(ms % 1000U);
60 out[2] = (int16_t)-(int16_t)(ms % 700U);
61 out[3] = (int16_t)(t % 30011U);
62 out[4] = (int16_t)(ms % 97U);
63 out[5] = (int16_t)-(int16_t)(ms % 89U);
68 static const int8_t z[] = {1, 10, 0, 1};
69 out[0] = (int8_t)((t / 2500U) % 50U);
70 out[1] = (int8_t)-(int8_t)((t / 2500U) % 40U);
80 {9085466, 8569150, 2007, 100009},
81 {8900000, 8330000, 1175, 94911},
82 {8500000, 7300000, -3022, 79839},
83 {9085466, 8569150, 2007, 100009},
88 v->
itow_ms = 300000000U + t / 1000U;
92 v->
lat_e7 = 378719000 + (int32_t)(t / 100000U);
94 v->
hmsl_mm = 100000 + (int32_t)(t / 50U);
104#define BS EMMC_BLOCK_SIZE
105#define DATA_START EMMC_LOG_DEFAULT_DATA_START
106#define CAPACITY (DATA_START + 8192U)
126 memcpy(buf, c->
blocks + (
size_t)lba *
BS, (
size_t)
n *
BS);
130 uint32_t
n, uint32_t budget) {
140 memcpy(c->
blocks + (
size_t)lba *
BS, buf, (
size_t)
n *
BS);
161 uint32_t c = 0xFFFFFFFFU;
162 for (
size_t i = 0; i <
n; ++i) {
164 for (
int k = 0; k < 8; ++k)
165 c = (c >> 1) ^ (0xEDB88320U & -(c & 1U));
169static uint32_t
rd32(
const uint8_t*
p) {
170 return (uint32_t)
p[0] | (uint32_t)
p[1] << 8 | (uint32_t)
p[2] << 16 |
171 (uint32_t)
p[3] << 24;
175 memset(
img, 0,
sizeof(*
img));
177 const uint8_t* s =
card->blocks;
182 uint32_t epoch =
rd32(s + 8), last_session = 0;
185 if (
rd32(
b) != 0x31474C53U ||
rd32(
b + 4) != epoch ||
rd32(
b + 8) != k)
192 if (
rd32(
b + 12) != last_session) {
193 last_session =
rd32(
b + 12);
196 unsigned used = (unsigned)(
b[18] |
b[19] << 8);
199 for (
unsigned o = 0;
o + 6 <= used &&
img->n <
max;) {
200 const uint8_t*
r =
b + 20 +
o;
204 x->t_ms =
rd32(
r + 2);
205 memcpy(x->data,
r + 6, x->len < 64 ? x->len : 64);
214#define MAX_RECS 40000U
215#define WAKE_LATENCY_US 37U
246static void sink(
void* ctx, uint8_t type,
const void* data, uint8_t len) {
251 if (i >= 0 && i < 3) {
256 else if ((int32_t)(t - w->
last_t[i]) <= 0)
261 uint32_t now_ms = w->
now / 1000U;
284 r->t_ms = w->
now / 1000U;
285 memcpy(
r->data, data, len);
292 for (
int i = 0; i < 7; ++i) {
293 w->
imu.
regs[0x20 + 2 * i] = (uint8_t)v[i];
294 w->
imu.
regs[0x21 + 2 * i] = (uint8_t)((uint16_t)v[i] >> 8);
318 memset(w, 0,
sizeof(*w));
358 for (
int i = 0; i < 4; ++i) {
365static int32_t
diff(uint32_t
a, uint32_t
b) {
366 return (int32_t)(
a -
b);
436#define EXPECT(cond, ...) \
439 snprintf(detail, sizeof(detail), __VA_ARGS__); \
444static void note(
const char* fmt, ...) {
460#ifdef FLIGHT_TEST_CANARY
472 double imu =
W.n_type[0] / s, hg =
W.n_type[1] / s, baro =
W.n_type[2] / s;
474 EXPECT(hg > 400 * 0.99 && hg < 400 * 1.01,
"high-g %.1f Hz", hg);
475 EXPECT(baro > 49 && baro < 51,
"baro %.1f Hz", baro);
476 note(
"IMU %.1f Hz, high-g %.1f Hz, baro %.1f Hz over %.0f s",
imu, hg, baro,
483 for (
unsigned i = 0; i <
W.n_produced; ++i) {
484 const rec_t*
r = &
W.produced[i];
486 memcpy(&t,
r->data, 4);
489 memcpy(&s,
r->data,
sizeof(s));
492 int16_t got[7] = {s.temp, s.gyro[0], s.gyro[1], s.gyro[2],
493 s.accel[0], s.accel[1], s.accel[2]};
494 EXPECT(memcmp(v, got,
sizeof(v)) == 0,
495 "IMU sample at t=%u us differs from the profile", t);
498 memcpy(&s,
r->data,
sizeof(s));
501 EXPECT(memcmp(
g, s.accel, 3) == 0,
502 "high-g sample at t=%u us differs from the profile", t);
505 memcpy(&s,
r->data,
sizeof(s));
510 s.temp_c100 ==
kBaro[
p].temp && s.pressure_pa ==
kBaro[
p].p;
512 s.pressure_pa >= 70000 && s.pressure_pa <= 110000;
514 "baro at t=%u us: %d.%02d C %d Pa, expected %d Pa", t,
515 s.temp_c100 / 100, abs(s.temp_c100 % 100), s.pressure_pa,
522 note(
"%u samples match the flight profile exactly",
n);
527 for (
int i = 0; i < 3; ++i)
528 EXPECT(
W.backwards[i] == 0,
"type 0x%02X went backwards %u times",
531 for (
unsigned i = 0; i <
W.n_produced; ++i) {
532 const rec_t*
r = &
W.produced[i];
534 memcpy(&t,
r->data, 4);
537 "IMU stamped %u us, not its edge", t);
540 "high-g stamped %u us, not its edge", t);
542 note(
"monotonic per sensor; IMU/high-g stamped at their data-ready edges");
547 EXPECT(
W.refused == 0,
"%u records refused by the log",
W.refused);
548 EXPECT(
W.log.stats.dropped == 0,
"log dropped %u",
W.log.stats.dropped);
549 unsigned j = 0, matched = 0;
550 for (
unsigned i = 0; i <
IMG.n; ++i) {
554 EXPECT(j <
W.n_produced,
"card has more records than were produced");
555 const rec_t* want = &
W.produced[j++];
559 "record %u on the card differs from what was produced", j);
562 EXPECT(matched ==
W.n_produced,
"%u produced, %u on the card",
W.n_produced,
564 note(
"all %u records on the card, byte for byte, in order", matched);
569 EXPECT(
IMG.super_ok,
"superblock missing or CRC bad");
570 EXPECT(
IMG.bad_blocks == 0,
"a log block failed its CRC");
571 EXPECT(
IMG.sessions == 1,
"%u sessions, expected 1",
IMG.sessions);
572 unsigned starts = 0, ends = 0, states = 0;
573 for (
unsigned i = 0; i <
IMG.n; ++i) {
578 EXPECT(starts == 1 && ends == 1,
"session start %u / end %u", starts, ends);
579 EXPECT(states == 2,
"%u STATE records, expected 2 (launch, recovery)",
582 note(
"%u blocks, superblock + every block CRC-valid, 1 session",
594 EXPECT(w.
n_type[0] > 833 * 20 * 99 / 100,
"IMU fell to %u samples",
598 EXPECT(
img.super_ok &&
img.bad_blocks == 0,
"card image damaged");
605 "no sealed history before the stall (cut %u ms)", cut);
606 unsigned before = 0, on_card = 0, open_lost = 0;
609 for (
unsigned i = 0; i <
img.n; ++i)
612 EXPECT(on_card == before,
"%u of %u sealed pre-stall records on the card",
619 for (
unsigned j = 0; j <
img.n && !found; ++j)
625 "%u writes timed out; sensors kept full rate; sealed data (< %u ms) "
626 "intact; open chunk lost %u records; disk %s, %u dropped, %u chunks "
637 faults_t f = {5000000, 5500000, 0, 0};
644 uint32_t prev = 0, gap = 0;
650 if (prev && t - prev > gap)
655 "IMU gap %u us", gap);
656 EXPECT(w.
n_type[1] == 400 * 20,
"high-g disturbed: %u samples",
659 note(
"IMU gap %.2f s, recovered after %u inits; high-g and baro unaffected",
675 "module config: model %u rate %u ms NMEA %d", w.
gps.
dynmodel,
678 unsigned checked = 0;
685 memset(&v, 0,
sizeof(v));
690 "fix at t=%u us differs from the GPS truth",
g.t_us);
691 EXPECT(
g.fix_type == 3 &&
g.num_sv == 10,
"fix type/SVs wrong");
693 (
g.pps_us > 0 &&
g.t_us -
g.pps_us < 1000000U),
694 "fix at %u us has a stale PPS %u",
g.t_us,
g.pps_us);
698 "cold 9600 -> 115200 -> 10 Hz NAV-PVT; %u fixes = truth, "
712 "IMU 833 Hz, high-g 400 Hz, baro ~50 Hz over a 20 s flight",
714 {
"sample_values",
"every sample equals the flight profile (raw, exact)",
716 {
"timestamps",
"monotonic; IMU/high-g stamped at the data-ready edge",
718 {
"conservation",
"every produced record is on the eMMC card, byte for byte",
721 "card image: superblock, block CRCs, one session, states",
724 "card times out 0.5 s: sensors unaffected, sealed data intact",
726 {
"gps",
"GNSS cold start 9600 -> 115200, 10 Hz fixes = truth, PPS-stamped",
729 "IMU dead 0.5 s: dropped, retried, recovered; others ok",
733int main(
int argc,
char** argv) {
734 const char* only =
NULL;
735 for (
int i = 1; i < argc; ++i) {
736 if (strcmp(argv[i],
"--list") == 0) {
737 for (
size_t c = 0; c <
sizeof(
checks) /
sizeof(
checks[0]); ++c)
741 if (strncmp(argv[i],
"--only=", 7) == 0)
745 for (
size_t c = 0; c <
sizeof(
checks) /
sizeof(
checks[0]); ++c) {
747 const char* hit = strstr(only,
checks[c].
id);
748 size_t len = strlen(
checks[c].
id);
749 if (!hit || (hit[len] !=
'\0' && hit[len] !=
','))
755 printf(
"CHECK %s %s %s | %s\n",
checks[c].
id,
ok ?
"PASS" :
"FAIL",
bool emmc_log_on_state(emmc_log_t *log, flight_state_t from, flight_state_t to, uint32_t now_ms)
emmc_status_t emmc_log_service(emmc_log_t *log, uint32_t now_ms)
emmc_status_t emmc_log_init(emmc_log_t *log, const emmc_port_t *port, void *context, const emmc_log_config_t *config, emmc_log_chunk_t *chunks, uint8_t chunk_count, uint8_t *scratch)
uint8_t emmc_log_backlog(const emmc_log_t *log)
bool emmc_log_append(emmc_log_t *log, uint8_t type, uint32_t t_ms, const void *data, uint8_t len)
emmc_log_config_t emmc_log_default_config(void)
#define EMMC_REC_USER_FIRST
#define EMMC_REC_SESSION_END
#define EMMC_REC_SESSION_START
static emmc_card_info_t card
static const emmc_port_t port
const uint16_t fake_baro_datasheet_prom[8]
dev_bus_t fake_baro_bus(fake_baro_t *f)
size_t fake_gnss_emit(fake_gnss_t *f, uint32_t now_us, uint8_t *out, size_t cap)
gnss_io_t fake_gnss_io(fake_gnss_t *f)
void fake_gnss_init(fake_gnss_t *f, uint32_t baud, fake_gnss_truth_t truth)
dev_bus_t fake_regs_bus(fake_regs_t *f)
void fake_regs_init(fake_regs_t *f, bool ms_bit)
static int32_t diff(uint32_t a, uint32_t b)
static void world_init(world_t *w, const faults_t *faults)
static void check_fault_imu_silent(void)
static int phase_at(uint32_t t)
static void check_emmc_stall(void)
static uint32_t crc32_ref(const uint8_t *p, size_t n)
static void fly(world_t *w)
static emmc_status_t c_write(void *ctx, uint32_t lba, const void *buf, uint32_t n, uint32_t budget)
static void gps_truth(uint32_t t, ubx_pvt_t *v)
static void service_card(world_t *w)
static void nominal(void)
static void check_gps(void)
static void note(const char *fmt,...)
static void highg_truth(uint32_t t, int8_t out[3])
static const struct @346030372276252055054377351274303231312311263244 kBaro[]
static void check_emmc_roundtrip(void)
static void check_timestamps(void)
static void set_chips(world_t *w, uint32_t t)
static void imu_truth(uint32_t t, int16_t out[7])
static void world_free(world_t *w)
static const check_t checks[]
static uint32_t rd32(const uint8_t *p)
static void check_conservation(void)
static void read_image(const card_t *card, image_t *img, unsigned max)
static emmc_status_t c_start(void *ctx, uint32_t *count)
static void change_state(world_t *w, flight_state_t to)
static emmc_status_t c_read(void *ctx, uint32_t lba, void *buf, uint32_t n, uint32_t budget)
static void sink(void *ctx, uint8_t type, const void *data, uint8_t len)
static void check_sample_values(void)
static void check_sensor_rates(void)
void gnss_core_feed(gnss_core_t *c, const uint8_t *bytes, size_t n, uint32_t now_us)
void gnss_core_set_pps(gnss_core_t *c, uint32_t edge_us)
void gnss_core_init(gnss_core_t *c, uint32_t now_us)
void gnss_core_poll(gnss_core_t *c, uint32_t now_us)
*brief create JSON pointer *sa reference_tokens const string_t const string_t & b
*brief create JSON pointer *sa reference_tokens const string_t & a
size_type count(const key_type &key) const
detail namespace with internal helper functions
*brief user defined string literal for JSON values *sa std::size_t n
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 void failed(sensors_device_t *d, int err, uint32_t now_us)
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_BARO_STEP_US
#define SENSORS_HIGHG_PERIOD_US
#define SENSORS_IMU_PERIOD_US
unsigned clear_after_reads
uint32_t card_fail_from_ms
uint32_t card_fail_until_ms
void(* record)(void *ctx, uint8_t type, const void *data, uint8_t len)
uint32_t sealed_before_stall_ms
emmc_log_chunk_t chunks[4]
#define UBX_DYNMODEL_AIR4