Motivation
The static study (csi-static-occlusion) deliberately froze the crowd — and its own
"next steps" flagged that the variance feature actually lives in motion: real CSI counting
(Choi/Ling) uses temporal statistics over a sliding window, not per-snapshot spread. This is
the first coupled experiment: a JuPedSim trajectory supplies time-varying body positions to
the ray tracer, producing a CSI time series rather than per-N snapshots. It is also the
first exercise of the exp-csi-crowd chain end-to-end on a real floor.
Design
- Simulation:
exp-csi-crowd(walk-notebook (from_floor) → sionna-csi-runner, wired 2026-06-04). The bakedfloor_walk.pywalks N agents across the real floor;trajectory_frame_stridesubsamples frames for the RT pass. - Sweep: crowd size (e.g. 5 / 10 / 20 agents) × band {2.4, 5.0} GHz; fixed floor + link.
- Outputs: per-frame
links.parquet(now a time axis) +csi.hdf5[frame, link, …].
Evaluation
- Temporal CV over sliding windows vs crowd size; dynamic-to-noise ratio (variance of consecutive-frame CSI differences) ∝ flow — the EasyCount-style operationalisation.
- Static↔temporal gap: compare the per-frame amplitude distribution here against the static csi-static-occlusion bundles at matched occupancy — does motion widen the bundle beyond placement variance?
- Trajectory-conditioned attenuation: link attenuation vs instantaneous occluder count per frame.
Prerequisite
A furnished floor (occupiable seats) + a Tx/Rx device layout. Everything else is part of the
chain since 2026-06-04: the launcher stages inputs/floor_geometry.json (walker) and
inputs/scene.json (channel) from the same PostGIS floor, the walk-notebook image
dispatches geometry: from_floor to the baked floor_walk.py, and its trajectory.parquet
feeds sionna-csi-runner in the shared workdir. Driven by c-csi-crowd-temporal.
Results (first coupled run, 2026-06-04; multi-agent analysed + adversarially verified)
One coupled run executed on resplan-12439-floor-0 (12 agents → 16 analysis-placed seats,
120 s, RT every 24th frame → 100 frames × 5 links). Two platform notes up front: the
registered exp-csi-crowd upstream is parametric-only, so the run was assembled from
platform components (gis export-floor → floor_walk.py → staged scene + trajectory →
sionna-csi-runner coupled mode); and floor_walk.py needed a spawn-distribution fix
(distribute_by_number) before JuPedSim would accept a 12-agent crowd.
- Coupled plumbing — MET. Frame-indexed
links.parquet+csi.hdf5[frame,…]+ the carried-through trajectory, no NaN/Inf — the first time-resolved synthetic CSI on a real dataset floor. - Body-loss in the time domain — demonstrated. The persistently-occluded
rx-bed2link (99/100 frames) carries a sustained −12.8 dB median dip, corr(occluders, amp) = −0.88. - Motion-variance / static↔temporal gap — NOT TESTABLE on this run. Adversarial
re-computation found the trajectory never settles: per-frame displacement pinned at
0.060 m/frame (≈1.2 m/s) for all 120 s, all 12 agents still moving at the last frame,
net displacement 0.6–9 m against 143 m walked — contradicting the run's own
walk-metrics.json("10 seated, 3.0 s mean time-to-seat"). Root cause:floor_walkbookkeeps "arrival" at 0.4 m but never terminates/parks the JuPedSim journey (dwell_unitsparsed, unused) — agents orbit their seats indefinitely. Additionally the milling geometry missed the in-room LOS sliver entirely (zero occlusion onrx-living), inverting the expected in-room-dominant signature.
Interpretation (sober). The run validates the coupled pipeline and shows per-body
blockage as a time-series signal; the campaign's core question stays open pending the
floor_walk dwell fix (terminate or park agents on arrival), a geometric pre-check that the
crowd actually crosses the in-room link, and seed/crowd-size replication. The
metrics-vs-trajectory contradiction was caught by independent re-computation — the scalar
floor alone would have passed. Full deep-dive (environment overlay, crowd animation, CSI
time series): notebooks/experiments/csi-crowd-temporal/01_temporal_csi_analysis.ipynb.
Rendered artefacts in _attachments/experiments/csi-crowd-temporal/.
Re-run (2026-06-04, dwell + door physics fixed)
Both fidelity defects were fixed in floor_walk.py and the campaign re-run at three crowd
sizes:
- Dwell/parking — on arrival an agent is parked at its seat: removed from the JuPedSim
integrator (
mark_agent_for_removal) but kept in the trajectory as a stationary scatterer (a seated person must keep occluding the channel). Exactly one row per (agent, frame): live before arrival, seat after. - Sealed doors — the walkable derivation buffered walls by
wall_depth/2 + 20 cmclearance, double-counting the agent body (JuPedSim enforces the radius itself) and squeezing 80 cm doors to ~13 cm — the actual root cause of the original "milling" (agents were stuck en route). Fixed: clearance 5 cm + explicitagent_radius_m = 0.10, plus an optionalscenario.spawn_bbox_cmso the crowd enters from the east side and genuinely traverses the floor (the first run's agents spawned next to their seats).
Corrected runs (seed 0, 180 s, east-entry, two-rate RT sampling — every 6th frame in the 0–30 s walking window, every 60th seated; 150 RT frames × 5 links each):
| crowd | seated | note |
|---|---|---|
| 4 | 4/4 | clean |
| 8 | 8/8 | clean |
| 14 | 11/14 | 3 stragglers on tight-door routes keep wandering — documented, slightly inflate seated-phase CV |
The walking→seated phase split is now derived from the trajectory itself (settle = last frame after which an agent never moves; flagship: 11/14 settled, last arrival t = 22.8 s, stragglers = agents {21, 22, 23}).
Measured outcomes (criteria verdicts — canonical numbers from the wired-chain sweep
01KT8WCGV890DBW7E7WF8EFZSZ, uniform stride 24 / 1.2 s sampling; the two-rate prototype
numbers were consistent and are superseded):
- C1 plumbing — MET at all six cells as single platform runs of the wired chain
(launcher-staged floor bundle + scene; frame-indexed links + carried trajectory; no
NaN/Inf;
gate_passed=true). - C2 motion variance ∝ crowd size — MET, now seed- and band-replicated: walking-phase
temporal CV is monotone in crowd size in every independent series — Spearman
ρ(size, CV) = +1.0 for each of seeds {0, 1, 2} at 2.4 GHz (sweep
01KT8X6N…) and +1.0 at 5.0 GHz (seed 0). Seed-0 magnitudes: mean-over-links 0.068 → 0.276 → 0.370, living-link 0.036 → 0.230 → 0.478 for 4 → 8 → 14 agents; cross-seed mean ± std in the campaign report. The in-room living link genuinely participates (east-entry traversal). - C3 static↔temporal gap — measured, with a structural caveat: walking-phase CV per link 0.036–0.706 vs seated-phase CV ≈ 10⁻⁷–10⁻¹⁶ — the parked crowd produces a literally constant channel (deterministic RT, no micro-motion), so the measured gap is the upper bound of the static-crowd confound. Real seated crowds (breathing, fidgeting) sit between these extremes — modelling parked-body micro-motion is the identified next fidelity step, and exactly the regime where BLE anchoring earns its keep.
The 5 GHz cells (3 runs) are in the same sweep, unanalysed in the notebook's flagship pass — the band axis is the ready-made follow-up.
Remaining caveats (re-run): single seed per size; per-body loss uncalibrated. — done 2026-06-04: the chain is now
exp-csi-crowd still
needs the from-floor walker wired inwalk-notebook (from_floor) → sionna-csi-runner with launcher-staged floor bundle + scene
from one PostGIS floor (verified end-to-end via sim run exp-csi-crowd).