Camera session manager developed for Subaru/SCExAO
- Install pyMilk first
- Clone with git.
pip install -e .at repository root.- Some features (EDT) will _require that the root is
$HOME/src/camstack. - Define
CAMSTACK_DEPLOYMENT_IDto benefit from a custom deployment, and seecamstack/deployment/__init__.pyfor how they are defined and used.
%%{init: {'flowchart': {'nodeSpacing': 25, 'rankSpacing': 50, 'useMaxWidth': false}}}%%
flowchart TD
A["camstart [name] [-slk]"]
subgraph CS["Camera Session"]
subgraph CC["Camera Control"]
direction LR
direction LR
CCInit[Init
- Configure camera
- Configure grabber
- Run dependents]
CCControl[Control
_exposed python prompt_
- Change crop
- Change exposure, etc
- Change trigger modes
- Any definition of camera]
CCPT[Polling thread
-Dependents status
-Temperature
-Other camera polls]
CCRedis[Opt: DB update]
CCSystemd[Opt: status & alerts]
CCPyro[Opt: RPC Control
Using Pyro, control
cam from any python
session]
end
subgraph D["Dependents"]
CF[Camera Framegrabbing]
AX1[Aux process 1]
AX2[Aux process 2]
end
end
A -->|"Start/kills in tmux_ctrl session"| CC
CC -->|"in tmux"| CF
CC -->|"in tmux"| AX1
CC -->|"in tmux"| AX2
CF -->|"data SHM (raw)"| AX1
AX1 -->|"data SHM (processed)"| AX2
linkStyle 4,5 stroke:red,stroke-width:3px
There are three ways of controlling the camera:
Directly at-the-counter
An interactive python shell is always running in the camera_ctrl tmux session. By attaching to this session, the operator can call all the exposed camera methods, e.g. set_tint(shutter_time), set_camera_mode(), set_fps(1e9), etc.
Tmux line injection
Using the tmux API, the commands above can be sent (shell, python's libtmux) using tmux send-keys into the ctrl session
Python remote objects
During the startup, the camera object can be bound to a Remote Procedure Call (RPC) object using the Pyro package.
This enables remote connection (local and remote) to the control object for any allowable location, and control the camera such as:
cam = connect('CAMERA_IDENTY')
cam.get_tint() # returns current exposure time
cam.set_camera_mode('MY_CUSTOM_CROP') # halts and restart the camera in a different sensor crop mode # the crop is pre-defined in the camera's class.
cam.set_fps(1000) # 1 kHzThe data pointer is a shared memory object, which is separate from the control object. See ImageStreamIO and the python bindings pyMilk At any python terminal on the local machine
from pyMilk.interfacing.shm import SHM
cam_data = SHM('cam_shm_name')
array = cam_data.get_data() # Get current data, maybe stale
array = cam_data.get_data(True) # Get next published data
array_list = cam.multi_recv_data(100) # Get next 100 framespygame based SHM viewers are distributed with camstack
The package an plugin system to customize their features.
pygame requires python <= 3.13
Running anycam.py <shm_name> (and press h for help):
The class hierarchy is built:
Base class
Common technical basis (e.g. same framegrabber)
Camera model: define control methods for this camera odel)
Camera identity: define features, keywords, for ONE camera doing ONE job.
Integrating new camera models is more or less easy depending on the existing, and how esoteric the manufacturer's SDK.
An entrypoint is created in the deployments/ folder for each camera identity, and then the session can be started using
camstart <camera_nickname>In many science cases, the camera framegrabbing is designed to be a C program with realtime privileges and minimal overhead. On the other hand, it is convenient to keep the camera control and session orchestrator purely in python.
Unfortunately, not here until we do a better review of
See details (probs not up to date)
Changing "mode" really means changing crop size. The framegrabber has to be reconfigured, all SHMs re-instantiated with their new size, etc. Pretty much all steps above are called in the same order.
This is done without quitting at the <cam>_ctrl command prompt, by calling set_camera_mode(some_predefined_mode_id).
For dumb cameras (acquisition channel but no control channel), the FG acquisition can be set to an arbitrary size dynamically by calling set_camera_size(height, width).
├── camstack/ # __python package root__
│ ├── acq/ # runnable files for acquisition
│ │ ├── flycapture_usbtake.py # with PyCapture2
│ │ ├── spinnaker_usbtake.py # with PySpin
│ │ └── simcam_framegen.py # frame emitter for simulated camera
│ ├── cam_mains/ # Exec entrypoint for camera personalities, with auxiliary helper processes
│ ├── cams/ # Camera class hierarchy
│ │ ├── base.py # Base class, defining all of a "camera session"
│ │ └── ... # Many more files following inheritance as:
│ │ # Base --> Framegrabber type --> Camera model --> Camera personality
│ ├── core/
│ ├── deployments/ # Deployment files defining which camera personalities are available in each install
│ ├── deprecatedscripts/
│ ├── image_processing.py # image processing routines
│ ├── main_arch.py # Schema definitions for deployment
│ ├── main.py # __main entrypoint__ for starting camera sessions
│ ├── pyro_keys.py
│ ├── scxkw.py # Wrapper around optional keyword + database system
│ ├── utilities/
│ ├── viewerclasses/ # Pygame viewers: classes for viewers tailored to different camera personalities
│ ├── viewermains/ # Pygame viewers: entrypoints for viewer applications
│ └── viewertools/ # Pygame viewers: a plugin stack for the pygame viewers
├── conf/
│ ├── modes/ # toml files containing e.g. crop modes
│ └── edt_fg_conf/ # cfg files for EDT framegrabbers (cameralink)
├── doc/ # Documentation
├── ocamdecode/ # Generation & config files for OCAM2
├── pyproject.toml
├── README.md
├── scripts/ # Custom utilities & legacy deployment scripts
├── setup.py
└── tests/ # pytest stack