API reference
Shared constants for the currentsensation package.
- class currentsensation.constants.CaptureConfig(width: int = 640, height: int = 480, fps: int = 25, device: str = '/dev/video0')[source]
Default camera and recording parameters.
- class currentsensation.constants.ExperimentTiming(shake_dur: float = 30.0, peri_stim_dur: float = 30.0, stim_dur: float = 1800.0, delay_offset: float = 5.0, reps: int = 3)[source]
Default time intervals for an experimental session.
All durations are in seconds.
- class currentsensation.constants.SessionPaths(root: Path)[source]
Container for output paths derived from a session save directory.
- classmethod from_root(root: Path | str) SessionPaths[source]
Build a SessionPaths from a root directory (created if missing).
GPIO control with pluggable backends.
The GPIOController exposes domain-level operations
(set_line_high, all_off …) and delegates the actual pin toggling
to a backend. Two backends ship with the package:
RPiBackend— real hardware viaRPi.GPIO(only importable on a Raspberry Pi).MockBackend— in-memory state for tests and dry-runs.
Picking the right backend is the responsibility of
make_default_controller(), which tries the real one and falls
back to the mock with a warning.
- class currentsensation.hardware.GPIOBackend(*args, **kwargs)[source]
Minimal interface a GPIO backend must implement.
- class currentsensation.hardware.GPIOController(backend: GPIOBackend, pin_map: dict[str, int] | None = None)[source]
Domain-level GPIO operations for the current-sensing rig.
- change_current_line(line: str) None[source]
Switch to
line: turn off all current lines first, then energise.
- class currentsensation.hardware.MockBackend(pin_state: dict[int, bool] = <factory>, transitions: list[~currentsensation.hardware.PinTransition] = <factory>)[source]
In-memory GPIO substitute for tests and dry-runs.
- transitions: list[PinTransition]
- class currentsensation.hardware.PinTransition(timestamp: float, pin: int, state: bool)[source]
A single recorded pin state change.
- class currentsensation.hardware.RPiBackend[source]
Backend that drives real Broadcom GPIO pins via
RPi.GPIO.
- exception currentsensation.hardware.UnknownLineError[source]
Raised when a line tag does not exist in the pin map.
- currentsensation.hardware.make_default_controller(pin_map: dict[str, int] | None = None, *, force_mock: bool = False) GPIOController[source]
Return a controller with the best available backend.
- Parameters:
pin_map – Optional override for the line→pin assignment.
force_mock – If True, never attempt the real backend.
- Returns:
A
GPIOControllerready to drive lines.
Video and image capture wrappers.
VideoCapture spawns an ffmpeg process for the requested duration;
ImageCapture grabs single frames via pygame.camera. Both expose
a uniform record() method that writes a single timestamped file
into the session directory.
The capture backends are interchangeable from the experiment orchestrator’s perspective — picking video vs images is a CLI decision.
- class currentsensation.capture.CaptureBackend(*args, **kwargs)[source]
Any capture backend the orchestrator can drive.
- class currentsensation.capture.ImageCapture(config: CaptureConfig = CaptureConfig(width=640, height=480, fps=25, device='/dev/video0'))[source]
pygame.camera-based single-frame grabber.
- class currentsensation.capture.NullCapture(extension: str = '.avi')[source]
No-op capture backend that only logs what would have been recorded.
- class currentsensation.capture.VideoCapture(config: CaptureConfig = CaptureConfig(width=640, height=480, fps=25, device='/dev/video0'))[source]
ffmpeg-based video recorder.
- config: CaptureConfig = CaptureConfig(width=640, height=480, fps=25, device='/dev/video0')
- currentsensation.capture.build_filename(save_dir: Path, line: str, extension: str) Path[source]
Compose <save_dir>/<timestamp>-<line><ext> with current time.
Build a deterministic, replayable experiment timeline.
The schedule is the single source of truth for what happens when. The orchestrator merely walks it; the hardware modules merely react. Splitting this out means you can dry-run an experiment, inspect the timeline, and unit-test it without ever touching a GPIO library.
The session layout follows the 2017 original:
`
[ off-trial ] ← baseline
[ rep 1: line A, B, C ] ← order randomised if requested
[ rep 2: line A, B, C ]
...
[ off-trial ] ← recovery baseline
[ finish ]
`
Each trial consists of a shake stimulus immediately followed by a
current line being switched on for stim_dur seconds, with a video
(or stream of images) recorded across the shake-and-stim interval.
- class currentsensation.scheduling.ScheduleConfig(timing: ~currentsensation.constants.ExperimentTiming = <factory>, lines: tuple[str, ...] = ('red', 'blue', 'yellow'), randomise_lines: bool = True, mode: ~typing.Literal['video', 'image'] = 'video', image_fps: int = 2)[source]
Inputs to
build_schedule().- timing: ExperimentTiming
- class currentsensation.scheduling.TimedEvent(offset_s: float, kind: ~typing.Literal['pin_high', 'pin_low', 'change_line', 'capture_video', 'capture_image', 'finish'], payload: dict = <factory>, priority: int = 2)[source]
A single thing-to-do at a given offset from session start.
- kind
What kind of event this is (drives orchestrator dispatch).
- Type:
Literal[‘pin_high’, ‘pin_low’, ‘change_line’, ‘capture_video’, ‘capture_image’, ‘finish’]
- currentsensation.scheduling.build_schedule(config: ScheduleConfig, rng: Random | None = None) list[TimedEvent][source]
Generate the full event timeline for one experimental session.
- Parameters:
config – Timing, line set, capture mode.
rng – Random source for line-order shuffling. Defaults to a fresh
random.Randomso tests can pass a seeded one.
- Returns:
List of
TimedEventsorted by(offset_s, priority).
- currentsensation.scheduling.default_schedule_path(save_dir: Path) Path[source]
Where the schedule text file lives within a session directory.
- currentsensation.scheduling.serialise_schedule(events: list[TimedEvent], out_path: Path, session_start: datetime | None = None) Path[source]
Write a human-readable schedule file mirroring the 2017 format.
Run an experimental session given a schedule and the hardware to drive.
- class currentsensation.experiment.Experiment(controller: GPIOController, capture: CaptureBackend, paths: SessionPaths)[source]
Drive a single experimental session.
- capture: CaptureBackend
- controller: GPIOController
- paths: SessionPaths
- run(config: ScheduleConfig, *, dry_run: bool = False) list[TimedEvent][source]
Build a schedule, write it to disk, and (optionally) execute it.
- Parameters:
config – What to schedule.
dry_run – If True, do not enter the scheduler loop; just write the schedule file and return the event list.
- Returns:
The sorted event list that was scheduled.
- exception currentsensation.experiment.ScheduleFinished[source]
Raised inside a scheduled callback to terminate the session cleanly.
Command-line entry point for currentsensation run.