|
openaliro
Aliro reader: UWB/CCC core and ESP32-S3/C5/C6 port
|
— CIA RX-diagnostics latch + [ALAB] emitter (channel-impulse Stage 0/1). More...
#include "uwb_cirdiag.h"#include <stdint.h>#include <string.h>#include <deca_device_api.h>#include "woz_log.h"#include "woz_port.h"Data Structures | |
| struct | cirdiag_rec |
| Channel impulse response diagnostic record: timestamp (microseconds), sample count, base index, and tap array (I/Q magnitude or signed values) for post-processing. More... | |
Macros | |
| #define | CIRDIAG_SYS_CFG 0x10UL |
| SYS_CFG + STS-packet-config (CP_SPC): the mode of THIS reception (0=SP0..3=SP3). | |
| #define | CIRDIAG_CIR_WIN 64u |
| Windowed-CIR dump width, in complex taps, centred on the first-path index. | |
| #define | CIRDIAG_CIR_EVERY 4u |
| Take a window on one Final in every N. | |
| #define | CIRDIAG_FP_FRAC_BITS 6u |
| ipatovFpIndex is Q10.6 (6 fractional bits); the integer sample index is the high bits. | |
| #define | CIRDIAG_CLK_CTRL 0x110004UL |
| CLK_CTRL_ID. | |
| #define | CIRDIAG_PROBE_TAPS 8u |
| Taps per probe pass. | |
| #define | CIRDIAG_RING_RECS 16u |
| Deferred-dump ring. | |
Functions | |
| static uint16_t | cirdiag_window_base (void) |
| Absolute Ipatov sample index of tap 0 for a window centred on the latched first path, clamped into the valid accumulator span [0, DWT_CIR_LEN_IP_PRF64 - WIN]. | |
| static void | cirdiag_drain (void) |
Emit every buffered window as ev=uwb.cir lines (oldest first), then empty the ring. | |
| uint32_t | uwb_cirdiag_ring_count (void) |
| Return the count of CIR windows currently in the ring buffer (0 to CIRDIAG_RING_RECS). | |
| void | uwb_cirdiag_set_enabled (bool on) |
| Enable or disable CIA diagnostic capture globally. | |
| bool | uwb_cirdiag_enabled (void) |
| Return true if CIA diagnostic capture is enabled. | |
| void | uwb_cirdiag_dump_set_enabled (bool on) |
| Arm or disarm CIR window capture. | |
| bool | uwb_cirdiag_dump_enabled (void) |
| Return true if CIR window dump is armed (enabled and CIA logging armed). | |
| bool | uwb_cirdiag_capture (uint32_t status, uint16_t datalength, bool deadline_pending) |
| Capture one reception's CIR diagnostic snapshot: arm CIA logging on first RX, then latch the status, frame length, STS quality/status, and (if window dump is enabled and the radio is idle) a fixed-size Ipatov-centred CIR window. | |
| bool | uwb_cirdiag_window_due (void) |
| Return true on every CIRDIAG_CIR_EVERY-th call if capture is enabled, CIA logging is armed, and window dump is armed; used to throttle window reads during streaming. | |
| void | uwb_cirdiag_probe (void) |
| Diagnostic: read and print the CIR at four sample offsets (three distinct addresses plus one repeat) to verify addressing and detect non-determinism. | |
| void | uwb_cirdiag_flush (void) |
| Emit the pending CIR snapshot: write the summary line ([ALAB] ev=uwb.diag) with Ipatov and STS peak/power/quality fields, and either defer the window to the ring buffer (if window dump is enabled) or skip it. | |
Variables | |
| static volatile bool | g_on |
| Runtime arm state (console-toggled; OFF at boot). | |
| static volatile bool | g_dump |
| Windowed-CIR dump arm (independent of g_on; OFF at boot). | |
| static bool | g_cia_armed |
| Chip-side CIA diagnostic logging enabled (lazily, on the RX path). | |
| static dwt_rxdiag_t | g_diag |
| Latched snapshot of the most recent reception (latest wins). | |
| static uint32_t | g_cir [CIRDIAG_CIR_WIN] |
| Windowed-CIR snapshot: DWT_CIR_READ_MID packs each tap as two int16 (real, imag) in one word, so g_cir doubles as an int16[2*WIN] pair array. | |
| static volatile uint32_t | g_seq |
| Seqlock around the snapshot: odd while the RX path writes; the flush copies only between two equal even reads. | |
| static uint32_t | g_win_tick |
| Decimation tick: the shims call this once per Final, so it advances per ranging block. | |
— CIA RX-diagnostics latch + [ALAB] emitter (channel-impulse Stage 0/1).
Split the work across the two contexts the ALAB contract demands: the RX callback only latches registers into a snapshot (uwb_cirdiag_capture, plain stores + one SPI read), and a task-side uwb_cirdiag_flush formats/prints the line. On the nRF the flush runs on the sysworkq (uwb_rxdiag.c submits it); on the ESP32 the pinned ISR-service task calls it after its IRQ drain loop, so capture and flush are sequential there. A seqlock covers the one real race (nRF: a new capture preempting a flush mid-copy): torn snapshots are dropped, the next reception re-latches.
Stage 1 adds an independently-armed windowed-CIR dump: when armed, capture also reads a fixed window of Ipatov complex taps centred on the first-path index into the snapshot. The taps are NOT printed on the RX/flush path — a full window is ~64 serial lines per reception, enough blocking UART to overrun the ranging slot and stall a live walk-up. Instead flush appends each window to a small RAM ring (the last CIRDIAG_RING_RECS receptions), and the taps are drained to ev=uwb.cir lines only when the dump is disarmed (uwb_cirdiag_dump_set_enabled (false)) — that runs in console/task context after the walk-up, so the unlock is unaffected while capturing. Deferring the printing was necessary but not sufficient: the window READ is itself too long to sit inside a live ranging block, where the responder still owes a POLL or Final reception. The shims pass that down as deadline_pending and the window is taken only on the Final. Nor was that sufficient: the accumulator cannot be read at all while the receiver is up, and the shim re-arms an SP0 listen the moment the Final is serviced, so the read has to happen BEFORE that (the shims gate it on ccc_shim_rx_awaiting_final). Doing it on every block then cost every range, so uwb_cirdiag_window_due decimates it to one Final in CIRDIAG_CIR_EVERY.
| #define CIRDIAG_CIR_EVERY 4u |
Take a window on one Final in every N.
The read is the most expensive thing this unit does — the driver walks the accumulator in CHUNK_CIR_NB_SAMP-sample chunks, three SPI transactions each — and doing it on every block cost every range of the walk-up (bench run 5: 16/16 windows came back as real CIR, and not one block produced a distance). Sampling every fourth block still fills the ring across an approach while leaving three blocks in four untouched.
| #define CIRDIAG_CIR_WIN 64u |
Windowed-CIR dump width, in complex taps, centred on the first-path index.
64 is enough to carry the leading edge + early multipath that separates inside/outside a door, while staying cheap on serial (~64 lines) and RAM (256 B in DWT_CIR_READ_MID: 1 word/tap).
| #define CIRDIAG_CLK_CTRL 0x110004UL |
CLK_CTRL_ID.
dwt_readcir ORs the ACC_MCLK_EN|ACC_CLK_EN bits in here on every call and never clears them; the probe reads it back so a failed force shows up as data, not inference.
| #define CIRDIAG_PROBE_TAPS 8u |
Taps per probe pass.
Small enough that three passes at different offsets stay cheap; the point is comparing them, not the width.
| #define CIRDIAG_RING_RECS 16u |
Deferred-dump ring.
flush() appends each armed reception's window here (a cheap memcpy, no UART) instead of printing it on the ranging path; the taps are emitted only on disarm (cirdiag_drain), off that path, so a live walk-up still unlocks while capturing. Overwrites oldest, so it holds the last RECS receptions — the near-door end of an approach (~272 B/record). Single-producer (flush) / single-consumer (drain-after-disarm): the drain runs only after g_dump is cleared, so in the intended "walk up, then dump off" workflow no live flush races it.
| #define CIRDIAG_SYS_CFG 0x10UL |
SYS_CFG + STS-packet-config (CP_SPC): the mode of THIS reception (0=SP0..3=SP3).
Same trick as uwb_rxdiag.c's RXDIAG_CP_SPC, duplicated to keep this unit freestanding.
|
static |
Emit every buffered window as ev=uwb.cir lines (oldest first), then empty the ring.
Called on dump disarm, off the ranging path. Blocking: up to RECS*WIN serial lines.
| bool uwb_cirdiag_capture | ( | uint32_t | status, |
| uint16_t | datalength, | ||
| bool | deadline_pending | ||
| ) |
Capture one reception's CIR diagnostic snapshot: arm CIA logging on first RX, then latch the status, frame length, STS quality/status, and (if window dump is enabled and the radio is idle) a fixed-size Ipatov-centred CIR window.
Returns true if capture succeeded; false on first RX or if already pending. Seqlock-protected; safe to call from RX callback.
| void uwb_cirdiag_dump_set_enabled | ( | bool | on | ) |
Arm or disarm CIR window capture.
When arming, also arms the summary diagnostics. When disarming, drains all buffered windows to the console via ev=uwb.cir lines and clears the ring.
| void uwb_cirdiag_flush | ( | void | ) |
Emit the pending CIR snapshot: write the summary line ([ALAB] ev=uwb.diag) with Ipatov and STS peak/power/quality fields, and either defer the window to the ring buffer (if window dump is enabled) or skip it.
Retry up to 3 times if the seqlock detects concurrent capture. Idempotent.
| void uwb_cirdiag_probe | ( | void | ) |
Diagnostic: read and print the CIR at four sample offsets (three distinct addresses plus one repeat) to verify addressing and detect non-determinism.
Requires CIA logging armed (one reception taken). Outputs one pass in MID mode (int16 real/imag) and one in FULL mode (raw 24-bit) at the base offset.
|
static |
Windowed-CIR snapshot: DWT_CIR_READ_MID packs each tap as two int16 (real, imag) in one word, so g_cir doubles as an int16[2*WIN] pair array.
g_cir_base is the absolute Ipatov sample index of tap 0; g_cir_have gates emission (false if dump disarmed or the read failed).