Commit Graph

161 Commits

Author SHA1 Message Date
ykcheungab 817f830565 record the full experiment: video, CSV and metadata
Phase 1 of the review asked for one directory per experiment, a force CSV,
video of the pre-processing image, and metadata that ties them together. The
record channel from the previous change already kept the whole session; this
adds the on-disk format.

What
- Add record/experiment.py: ExperimentRecorder writes one directory per
  session containing session.json, forces.csv, frames.csv, events.csv,
  raw_NNNN.mp4 (segmented) and an optional lossless PNG sequence.
- forces.csv uses the reviewed column order
  (session_id, frame_id, t_rel_s, FN_raw, FX_raw, FY_raw, FN_N, FX_N, FY_N,
  valid, calibration_id) and leaves a cell empty when a value is unavailable:
  an uncalibrated newton value or a failed readout is never written as 0.
- frames.csv is the frame-index mapping: frame_id, t_rel_s, acquisition wall
  clock, segment, segment_frame, in_video. forces.csv and frames.csv share the
  packet frame_id and the same t_rel_s origin, so video and data can be
  aligned after dropped or segmented frames.
- session.json carries format version, session id, start/end time in UTC,
  device and config (with a sha256 of the config file), app/shared/SDK
  versions, calibration reference, axis convention, filter parameters, video
  codec and the lossy-MP4 caveat, plus the event log and end-of-run statistics.
  It is written once at start so a crash still leaves identifiable metadata.
- Add a record control to the toolbar: mode (video+data / data only) and a
  start/stop toggle that stays enabled while recording.
- Log zeroing, calibration application and session end as timestamped events.

Why
- The recorder is fed from the worker sink list on the acquisition thread and
  only appends to a bounded queue, so disk I/O can never stall acquisition.
- On_packet copies the raw frame instead of holding a reference: the packet
  frame may alias a buffer the SDK reuses between frames.
- A queue overflow is counted and alarmed (rate limited), and a write or encode
  failure is counted, reported and after 5 consecutive errors stops the
  recording with failed=true rather than silently losing data.

Verification
- shared/tests/test_experiment_recorder.py: 12 cases covering the directory
  layout, the CSV contract, frame-id/time-base agreement, segment rolling,
  data-only and lossless modes, queue overflow accounting, encoder failure,
  metadata fields, crash-before-stop, and that on_packet never blocks.
- tools/ui_check.py gains an "experiment record" section (169 checks, 0
  failures overall), driving a recording through the UI and asserting the
  layout, the empty-cell rule and the event sequence.
- apps/studio/tests/test_ui_smoke.py mirrors those invariants.
- docs/record-channel.md documents the format and the remaining phase-2 work.

Not in this change: CSV export of a selected time range, full-session curve
browsing, calibration diagnostics (outlier exclusion, fit residuals) and
PyOpenGL 3D.
2026-09-13 13:14:10 +08:00
ykcheungab f501fa93f3 subject: fix calibration cancel, unit mixing, FN clamping, export/apply and depth scale 2026-09-13 12:35:05 +08:00
ykcheungab db228b6f57 subject: store the whole session and drive calibration from the acquisition stream 2026-09-13 12:34:47 +08:00
ykcheungab f16d2b3d84 subject: add packet validity flags and a record-channel sink fan-out 2026-09-13 12:34:19 +08:00
ykcheungab 85d700bc06 fix stop button disabled during connection 2026-09-12 20:27:34 +08:00
ykcheungab 904c27125f changed customer to studio 2026-09-12 14:03:00 +08:00
ykcheungab a2db648560 dsh ui improve: add brand bar 2026-09-12 13:51:05 +08:00
ykcheungab 803a08fc78 dsh UI improvement 2026-09-12 13:26:39 +08:00
ykcheungab 705eaabcca update to match calibration data handling 2026-09-10 21:50:36 +08:00
zhangyijian a4766abee8 update arrow rotate control 2026-09-07 14:24:30 +08:00
ykcheungab e2d5e69229 initial commit, functinal software 2026-09-06 21:57:50 +08:00