47 uint32_t*
now =
nullptr;
97 return W->receiver.receive(buf, max_len, len);
100 return W->setup.send(bytes, len);
111 b.raw_counts =
W->battery_counts;
112 b.sampled_at_ms = now_ms;
124 static_cast<uint8_t
>(mv),
125 static_cast<uint8_t
>(mv >> 8),
126 static_cast<uint8_t
>(up),
127 static_cast<uint8_t
>(up >> 8),
128 static_cast<uint8_t
>(up >> 16),
129 static_cast<uint8_t
>(up >> 24)};
134 uint32_t before =
W->now;
136 W->board +=
W->now - before;
146 uint8_t c =
W->queue[0];
147 memmove(
W->queue,
W->queue + 1, --
W->queued);
155 while (
W->air.dio1()) {
159 (void)
W->setup.rearm_rx();
166 if (
W->queued <
sizeof W->queue)
167 W->queue[
W->queued++] = c;
189 (
void)
W->setup.start_rx_continuous();
202 g.len =
static_cast<uint8_t
>(
n);
224 static const idle_config_t icfg = {10000, 500, 500, 7000, 6600, 100};
257 w.
now +
static_cast<uint32_t
>(
static_cast<uint64_t
>(ticks) *
265 uint32_t steps =
static_cast<uint32_t
>(
266 static_cast<uint64_t
>(wake - w.
now) *
kTrueLsiHz / (128u * 1000u));
282#define EXPECT(cond, ...) \
286 snprintf(detail, sizeof detail, __VA_ARGS__); \
291void note(
const char* fmt, ...) {
307 for (
unsigned i = 0; i < w.
air.
n_sent &&
n < cap; ++i) {
332 EXPECT(
n >= 12 &&
n <= 13,
"%u heartbeats in 120 s",
n);
334 for (
unsigned i = 1; i <
n; ++i) {
335 uint32_t gap =
b[i].t -
b[i - 1].t;
336 uint32_t err = gap > 10000 ? gap - 10000 : 10000 - gap;
340 EXPECT(worst <= 500,
"a heartbeat interval was off by %u ms", worst);
341 for (
unsigned i = 0; i <
n; ++i) {
343 "heartbeat %u: state %u, %u mV", i,
b[i].
state,
b[i].mv);
344 uint32_t true_s =
b[i].t / 1000u;
345 EXPECT(
b[i].uptime_s + 1 >= true_s &&
b[i].uptime_s <= true_s + 1,
346 "heartbeat %u uptime %u s at %u s", i,
b[i].uptime_s, true_s);
349 "%u heartbeats, every 10 s +/- %u ms, decoded by Ryan's decode_frame,"
350 " 7.92 V, uptime counts sleep",
362 "%u sleeps, radio sniffing (SetRxDutyCycle) with RF_SW on receive "
379 EXPECT(
o.sensors &&
o.gps && !
o.idle_loop,
"ARMED does not start sensors");
387 EXPECT(
n == 3,
"%u heartbeats before the Arm (expected 0, 10, 20 s)",
n);
389 "Arm confirmed at 28.9 s: woke on DIO1 twice, ARMED %u ms later, "
390 "sensors + eMMC on, heartbeats stop",
403 for (
unsigned i = 0; i <
n; ++i) {
404 EXPECT(
b[i].mv == 0,
"heartbeat %u reports %u mV on USB", i,
b[i].mv);
407 "Idle judged USB as a battery");
408 note(
"on USB: %u heartbeats with battery_mV 0, Idle state USB",
n);
421 EXPECT(
failures == 2,
"%u consecutive failures counted, expected 2",
425 "%u sends after recovery: a retry storm",
428 "failure count did not reset after a good send");
431 "2 TxDone timeouts counted, no retries, back in RX each time, "
432 "recovered on the next heartbeat");
439 int32_t err =
static_cast<int32_t
>(w.
board - w.
now);
442 static_cast<uint32_t
>(115000ull * 32000u /
kTrueLsiHz) - 115000u;
443 EXPECT(err >= -50 && err <= 50,
"board clock off by %d ms after %u s", err,
446 "LSI %u Hz: board clock within %d ms of true time over %u s "
447 "(assuming 32 kHz would be ~%u ms off)",
459 "Idle: a LoRa heartbeat every 10 s, 7.92 V, decoded by Ryan's frames",
462 "Idle: radio sniffing (duty cycle, RF_SW) through every sleep",
465 "Idle: ground Arm wakes on DIO1 -> ARMED, sensors + eMMC start",
467 {
"idle_usb",
"Idle on USB: heartbeats never report USB's 5 V as the pack",
470 "Idle: TxDone timeouts counted, no retry storm, radio back in RX",
472 {
"idle_clock",
"Idle: RTC on a 30.5 kHz LSI, board clock still true time",
478int main(
int argc,
char** argv) {
479 const char* only =
nullptr;
480 for (
int i = 1; i < argc; ++i) {
481 if (strcmp(argv[i],
"--list") == 0) {
482 for (
const Check& c :
checks)
483 printf(
"%s\t%s\n", c.id, c.what);
486 if (strncmp(argv[i],
"--only=", 7) == 0)
490 for (
const Check& c :
checks) {
493 snprintf(list,
sizeof list,
",%s,", only);
495 snprintf(key,
sizeof key,
",%s,", c.id);
496 if (!strstr(list, key))
502 printf(
"CHECK %s %s %s | %s\n", c.id,
failed ?
"FAIL" :
"PASS", c.what,
bool packet_arrives(const uint8_t *bytes, uint8_t n)
uint32_t tx_timeouts() const
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_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)
emmc_log_config_t emmc_log_default_config(void)
static const emmc_port_t port
static ActuatorControllerState state
static const check_t checks[]
flight_state_t flight_state_next(flight_state_t state, flight_event_t event)
Over-the-air frame format for the LoRa command link.
idle_result_t idle_on_wake(idle_t *idle, uint32_t now_ms)
const idle_heartbeat_t * idle_last_heartbeat(const idle_t *idle)
bool idle_enter(idle_t *idle, uint32_t now_ms)
bool idle_init(idle_t *idle, const idle_config_t *config, const idle_port_t *port, void *context)
@ IDLE_EVENT_ARM_REQUESTED
*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
Glue: radio_receive() -> decode_frame() -> ArmGate.
mode_route_t mode_route_command(flight_state_t state, mode_command_t command, flight_state_t *next)
mode_outputs_t mode_outputs(flight_state_t state)
#define MODE_SNIFF_SLEEP_MS
unsigned beats(const World &w, Beat *out, unsigned cap)
void check_idle_rx_while_asleep()
void check_idle_heartbeat()
emmc_status_t no_io(void *, uint32_t, void *, uint32_t, uint32_t)
emmc_status_t no_card(void *, uint32_t *n)
constexpr uint32_t kTrueLsiHz
constexpr uint32_t kCounts7V92
void note(const char *fmt,...)
void ground_arm(World &w, uint32_t t, uint16_t seq)
void set_state(flight_state_t to)
void check_idle_tx_fail()
idle_command_t poll_command(void *)
void check_idle_arm_wake()
self_test_reply_t send_heartbeat(void *, const idle_heartbeat_t *hb, uint32_t)
constexpr uint8_t kTargetId
constexpr uint32_t kBootMs
void world_init(World &w)
emmc_status_t no_write(void *, uint32_t, const void *, uint32_t, uint32_t)
self_test_battery_t read_battery(void *, uint32_t now_ms, uint32_t)
detail namespace with internal helper functions
*brief user defined string literal for JSON values *sa std::size_t n
FrameStatus encode_frame(FrameType type, uint16_t seq, const uint8_t *payload, uint8_t payload_len, uint8_t *out, size_t out_cap, size_t &out_len)
Build a frame into out[0..out_cap).
void put_u32(uint8_t *p, uint32_t v)
RadioStatus radio_receive(uint8_t *buf, uint8_t max_len, uint8_t &len)
constexpr uint8_t kHeartbeatLen
Required payload length per type.
RadioStatus radio_probe()
FrameStatus decode_frame(const uint8_t *buf, size_t len, Frame &out)
Parse and validate a received packet.
uint32_t get_u32(const uint8_t *p)
uint16_t get_u16(const uint8_t *p)
RadioStatus radio_send(const uint8_t *bytes, uint8_t len)
constexpr uint32_t kArmKey
PLACEHOLDER: lead to choose the real key before flight.
constexpr LoraConfig kDefaultLora
static void failed(sensors_device_t *d, int err, uint32_t now_us)
static uint32_t until(uint32_t now, uint32_t t)
uint32_t sleep_elapsed_ms(uint32_t from_steps, uint32_t to_steps, uint32_t lsi_hz)
uint16_t sleep_wut_reload(uint32_t ms, uint32_t lsi_hz)
void set_rx_switch(bool receive) override
void delay_ms(uint32_t ms) override
struct anonymous_namespace{idle_e2e.cpp}::World::Ground ground[4]
uint8_t scratch[EMMC_BLOCK_SIZE]
emmc_log_chunk_t chunks[2]
idle_battery_state_t battery
uint8_t consecutive_send_failures
uint8_t payload[kFrameMaxPayload]
static std::vector< uint8_t > frame(uint8_t hi, uint8_t lo, size_t payload_len, uint8_t fill)
One wire packet: len(4, LE) then len bytes of body, where the first 4 body bytes are ty/packet_id(2)/...