Skip to content

Detect runtime mode switches in segmenter, emit per-mode functions#149

Merged
widgetii merged 1 commit intomasterfrom
feat/mode-switch-capture
May 3, 2026
Merged

Detect runtime mode switches in segmenter, emit per-mode functions#149
widgetii merged 1 commit intomasterfrom
feat/mode-switch-capture

Conversation

@widgetii
Copy link
Copy Markdown
Member

@widgetii widgetii commented May 3, 2026

Closes the fourth real gap from #145's plan-vs-shipped audit, with an empirical caveat documented in the docs.

Summary

  • trace_segment.py now detects runtime mode switches (0x100=0 ... 0x100=1 cycles after init) and emits them as mode_switch_N phases
  • trace_to_driver.py emits one <sensor>_set_mode_N function per detected mode switch, same shape as _linear_init
  • tools/test_pipeline.sh extended with a synthetic mode-switch fixture; CI asserts the function is emitted
  • Empirical investigation of mode-switch capture on SC2315E + Sofia documented in docs/sensor-driver-extraction.md

Why

#145's plan called for capturing init plus mode/resolution switches plus runtime AE. #145 shipped init + AE; mode switches were never wired up. This finishes that.

Empirical caveat (worth reading before merging)

Capture-side, mode switches need a streamer that supports runtime sensor reconfiguration:

  • OpenIPC Majestic does not — config goes through /etc/sensors/*.ini files and requires a streamer restart. Each mode is a separate cold-init capture, no in-trace mode switches possible.
  • XiongMai Sofia does, via the DVR-IP TCP protocol on port 34567. python-dvr exposes the knobs.

Whether a given knob actually triggers a sensor-side reconfigure (vs just software-side AE) is sensor-specific. I tested BroadTrends.AutoGain on the SC2315E test camera at 10.216.128.106:

cam.set_info(\"Camera.ParamEx.[0]\", {\"BroadTrends\": {\"AutoGain\": 1, \"Gain\": 50}})

Toggled 0→1→0 while ipctool trace was running. Result: zero additional 0x100 cycles in the trace after init. Sensor stays in linear mode regardless. Sofia's supported-sensor list (decompile at Sofia.c:41394) confirms it: SC2315_WDR is a separate entry from SC2315E — there's no WDR firmware for our SoC + sensor combo.

So the segmenter and generator are validated end-to-end on the synthetic fixture (tools/test_pipeline.sh). Live validation with a multi-mode sensor is deferred to whoever has hardware where Sofia drives a sensor with a _WDR variant (SC2315 plain, IMX307, etc.).

Heuristic and known blind spot

init_end (0x100=1) ──> ... ──> 0x100=0 ──> reconfigure ──> 0x100=1 ──> ...
                               └──────── mode_switch_1 ────────┘

If a sensor hot-swaps modes via group-hold (e.g. 0x3812=0x00 ... reconfigure ... 0x3812=0x30) without toggling 0x100, this heuristic misses it. Extend the segmenter when you hit such a sensor.

Regression check

Pipeline re-run on the SC2315E + Majestic capture from #145:

  • _linear_init: 172 writes, 169 unique regs, 100% / 100% / 100% vs widgetii/smart_sc2315e/sc2315e_sensor_ctl.c::sc2315e_linear_1080P30_init (unchanged)
  • 0 mode switches detected (correctly — sc2315e doesn't have one)
  • 3 functions emitted: _linear_init, _post_init_exposure_prime, _ae_step

The new code path doesn't fire on traces without mode switches, so existing behaviour is preserved.

Test plan

  • tools/test_pipeline.sh synthetic fixture has one mode switch; passes
  • Generated C compiles with gcc -Wall -Wextra -fsyntax-only and gcc -c
  • Empirical SC2315E capture: 0 mode_switch_N phases (matches the no-WDR-firmware finding)
  • CI test-extraction-pipeline job catches regressions

🤖 Generated with Claude Code

Closes the fourth real gap from #145's plan-vs-shipped audit, with one
caveat documented in the docs and tested empirically below.

trace_segment.py: new find_mode_switches() walks the trace after
init_end watching for `0x100=0 ... 0x100=1` cycles. Each cycle becomes
a mode_switch_N phase. The runtime/post_init anchors move to the last
0x100=1, so traces without a mode switch are unchanged.

trace_to_driver.py: emits one `<sensor>_set_mode_N` function per
mode-switch phase, same shape as `_linear_init`.

test_pipeline.sh: synthetic fixture extended with a mode switch
(0x100=0 / VMAX-rewrite / 0x100=1). CI asserts `_set_mode_1` is
emitted in the generated C and that it still passes
gcc -Wall -Wextra and the self-diff.

Empirical caveat: capture-side, mode switches require a streamer
that supports runtime sensor reconfiguration. Majestic does not
(config goes through .ini files + restart, each mode is a separate
cold-init capture). Sofia does, via the DVR-IP protocol; python-dvr
exposes the knobs. But whether a given knob causes a sensor-side
reconfigure is sensor-specific - Sofia's BroadTrends path lands in
software-side gain on most sensors. On SC2315E specifically,
toggling AutoGain 0->1->0 while ipctool trace was watching produced
*zero* additional 0x100 cycles - the sensor stays in linear mode.
Sofia's supported-sensor list confirms `SC2315_WDR` is a separate
entry from `SC2315E`, so no WDR firmware exists for that combo.
The segmenter and generator are validated end-to-end on the
synthetic fixture; live validation with a multi-mode sensor is
deferred to whoever has hardware where Sofia drives a sensor with
a `_WDR` variant.

Docs: new "Capturing mode switches" section in
sensor-driver-extraction.md walks through Majestic vs Sofia, the
python-dvr API, the empirical SC2315E finding, and the heuristic's
known blind spot (group-hold based hot swaps that don't toggle 0x100).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@widgetii widgetii merged commit cc1c04d into master May 3, 2026
3 checks passed
@widgetii widgetii deleted the feat/mode-switch-capture branch May 3, 2026 14:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant