Bluesky hardware abstraction library with EPICS control system integration for scientific instrument automation.
—
Comprehensive support for 2D area detectors including cameras, plugins for data processing, file I/O, and triggering strategies for coordinated data acquisition. Area detectors form a modular system where cameras capture images and plugins process, display, and save data.
Foundation classes that provide the core area detector functionality.
class DetectorBase(Device):
"""
Base class for all area detectors.
Parameters:
- prefix (str): EPICS PV prefix for detector
- name (str): Detector name
- read_attrs (list): Attributes to include in read()
- configuration_attrs (list): Configuration attributes
"""
def __init__(self, prefix, *, read_attrs=None, configuration_attrs=None, **kwargs): ...
def stage(self):
"""
Stage detector for data acquisition.
Returns:
list: Staged components
"""
def unstage(self):
"""
Unstage detector after acquisition.
Returns:
list: Unstaged components
"""
def trigger(self):
"""
Trigger detector acquisition.
Returns:
StatusBase: Trigger completion status
"""
class AreaDetector(DetectorBase):
"""
Generic area detector with camera and plugin support.
Provides a complete area detector system with camera control
and plugin chain for data processing and file writing.
"""
def __init__(self, prefix, *, name, **kwargs): ...
# Core components (automatically created)
cam = Component(AreaDetectorCam, 'cam1:')
image = Component(ImagePlugin, 'image1:')
def stage(self):
"""Stage detector and plugins for acquisition."""
def unstage(self):
"""Unstage detector and clean up."""
class SimDetector(DetectorBase):
"""
Simulated area detector for testing and development.
Provides all the functionality of a real detector but generates
synthetic data instead of requiring hardware.
"""
def __init__(self, prefix, *, name, **kwargs): ...
class ADBase(Device):
"""
Base class providing core area detector functionality.
Contains common PVs and methods used by all area detector components.
"""
def __init__(self, prefix='', **kwargs): ...Camera interfaces for different detector hardware types.
class CamBase(ADBase):
"""
Base camera class with common camera functionality.
Provides standard camera controls like acquire, trigger mode,
image size, and exposure time.
"""
def __init__(self, prefix, **kwargs): ...
# Standard camera controls
acquire = Component(EpicsSignal, 'Acquire', trigger_value=1)
acquire_time = Component(EpicsSignal, 'AcquireTime')
acquire_period = Component(EpicsSignal, 'AcquirePeriod')
# Image size and binning
size_x = Component(EpicsSignal, 'SizeX')
size_y = Component(EpicsSignal, 'SizeY')
bin_x = Component(EpicsSignal, 'BinX')
bin_y = Component(EpicsSignal, 'BinY')
# Trigger controls
trigger_mode = Component(EpicsSignal, 'TriggerMode')
image_mode = Component(EpicsSignal, 'ImageMode')
num_images = Component(EpicsSignal, 'NumImages')
class AreaDetectorCam(CamBase):
"""Generic area detector camera."""
def __init__(self, prefix, **kwargs): ...
class SimDetectorCam(CamBase):
"""Simulated detector camera with synthetic image generation."""
def __init__(self, prefix, **kwargs): ...
# Simulation-specific controls
sim_mode = Component(EpicsSignal, 'SimMode')
gain_x = Component(EpicsSignal, 'GainX')
gain_y = Component(EpicsSignal, 'GainY')
# Specific detector camera classes (30+ types available)
class AndorCam(CamBase):
"""Andor camera interface."""
def __init__(self, prefix, **kwargs): ...
class PerkinElmerCam(CamBase):
"""PerkinElmer detector camera."""
def __init__(self, prefix, **kwargs): ...
class PointGreyCam(CamBase):
"""Point Grey camera interface."""
def __init__(self, prefix, **kwargs): ...
class AdscCam(CamBase):
"""ADSC detector camera."""
def __init__(self, prefix, **kwargs): ...
class PilatusDetectorCam(CamBase):
"""Pilatus detector camera."""
def __init__(self, prefix, **kwargs): ...Plugins provide data processing, visualization, and file I/O capabilities.
class PluginBase(ADBase):
"""
Base class for all area detector plugins.
Plugins process data from cameras or other plugins in a pipeline.
Parameters:
- suffix (str): Plugin PV suffix (e.g., 'image1:', 'HDF1:')
"""
def __init__(self, prefix, **kwargs): ...
# Plugin control
enable = Component(EpicsSignal, 'EnableCallbacks')
nd_array_port = Component(EpicsSignal, 'NDArrayPort', string=True)
def enable_on_stage(self):
"""Enable plugin when parent detector is staged."""
def disable_on_unstage(self):
"""Disable plugin when parent detector is unstaged."""
class ImagePlugin(PluginBase):
"""
Image display plugin for live viewing.
Provides image data for display systems and live feedback.
"""
def __init__(self, prefix, **kwargs): ...
class StatsPlugin(PluginBase):
"""
Statistics plugin for image analysis.
Calculates min, max, mean, sigma, and other statistics
on image data in real-time.
"""
def __init__(self, prefix, **kwargs): ...
# Statistics outputs
min_value = Component(EpicsSignalRO, 'MinValue_RBV')
max_value = Component(EpicsSignalRO, 'MaxValue_RBV')
mean_value = Component(EpicsSignalRO, 'MeanValue_RBV')
sigma_value = Component(EpicsSignalRO, 'SigmaValue_RBV')
total = Component(EpicsSignalRO, 'Total_RBV')
class ROIPlugin(PluginBase):
"""
Region of Interest plugin for extracting image sub-regions.
Defines rectangular regions for analysis and processing.
"""
def __init__(self, prefix, **kwargs): ...
# ROI definition
min_x = Component(EpicsSignal, 'MinX')
min_y = Component(EpicsSignal, 'MinY')
size_x = Component(EpicsSignal, 'SizeX')
size_y = Component(EpicsSignal, 'SizeY')
class TransformPlugin(PluginBase):
"""
Transform plugin for image rotation, mirroring, and scaling.
"""
def __init__(self, prefix, **kwargs): ...
# Transform controls
transform_type = Component(EpicsSignal, 'Type')
origin_location = Component(EpicsSignal, 'Origin')Plugins for saving detector data to various file formats.
class FilePlugin(PluginBase):
"""
Base class for file writing plugins.
Provides common file writing functionality including
path management, file naming, and write control.
"""
def __init__(self, prefix, **kwargs): ...
# File path and naming
file_path = Component(EpicsSignal, 'FilePath', string=True)
file_name = Component(EpicsSignal, 'FileName', string=True)
file_template = Component(EpicsSignal, 'FileTemplate', string=True)
file_number = Component(EpicsSignal, 'FileNumber')
# File writing control
capture = Component(EpicsSignal, 'Capture')
num_capture = Component(EpicsSignal, 'NumCapture')
def stage(self):
"""Configure file plugin for acquisition."""
def unstage(self):
"""Clean up after acquisition."""
class HDF5Plugin(FilePlugin):
"""
HDF5 file writing plugin.
Saves detector data in HDF5 format with comprehensive metadata
and support for compression and chunking.
"""
def __init__(self, prefix, **kwargs): ...
# HDF5-specific controls
compression = Component(EpicsSignal, 'Compression')
store_attr = Component(EpicsSignal, 'StoreAttr')
store_perform = Component(EpicsSignal, 'StorePerform')
class TIFFPlugin(FilePlugin):
"""
TIFF file writing plugin.
Saves individual images as TIFF files with standard formatting.
"""
def __init__(self, prefix, **kwargs): ...
class NetCDFPlugin(FilePlugin):
"""
NetCDF file writing plugin for scientific data formats.
"""
def __init__(self, prefix, **kwargs): ...
class JPEGPlugin(FilePlugin):
"""
JPEG file writing plugin for compressed images.
"""
def __init__(self, prefix, **kwargs): ...
# JPEG-specific controls
quality = Component(EpicsSignal, 'Quality')Classes that define different triggering strategies for coordinated data acquisition.
class TriggerBase:
"""
Base trigger interface for area detectors.
Defines the trigger protocol that detectors must implement
for integration with data acquisition systems.
"""
def trigger(self):
"""
Trigger detector acquisition.
Returns:
StatusBase: Status tracking trigger completion
"""
raise NotImplementedError()
class SingleTrigger(TriggerBase):
"""
Single frame trigger strategy.
Configures detector for single image acquisition per trigger.
"""
def trigger(self):
"""Trigger single frame acquisition."""
# Set single image mode
# Start acquisition
# Return status
def stage(self):
"""Configure for single trigger mode."""
class MultiTrigger(TriggerBase):
"""
Multiple frame trigger strategy.
Configures detector for multiple image acquisition per trigger.
"""
def __init__(self, num_images=1, **kwargs):
self.num_images = num_images
super().__init__(**kwargs)
def trigger(self):
"""Trigger multi-frame acquisition."""
def stage(self):
"""Configure for multi-trigger mode."""
class ContinuousAcquisitionTrigger(TriggerBase):
"""
Continuous acquisition trigger for streaming data.
Starts continuous acquisition and manages data collection
over specified time periods.
"""
def trigger(self):
"""Start continuous acquisition."""
def stage(self):
"""Configure for continuous mode."""Mixins for integrating with data management systems and event-based data access.
class FileStoreBase:
"""
Base class for FileStore integration.
Provides integration with document-based data systems
for tracking detector data files and metadata.
"""
def generate_datum(self, key, timestamp, uid):
"""
Generate datum document for data file.
Parameters:
- key (str): Data key identifier
- timestamp (float): Data timestamp
- uid (str): Unique identifier
Returns:
str: Datum ID for referencing data
"""
class FileStoreHDF5(FileStoreBase):
"""
FileStore integration for HDF5 files.
Manages HDF5 file metadata and provides data access
through the document model.
"""
def __init__(self, *args, **kwargs): ...
class FileStoreTIFF(FileStoreBase):
"""
FileStore integration for TIFF files.
"""
def __init__(self, *args, **kwargs): ...from ophyd.areadetector import AreaDetector, SingleTrigger
# Create detector with single trigger capability
class MyDetector(SingleTrigger, AreaDetector):
pass
detector = MyDetector('XF:28IDC:DET:', name='detector')
detector.wait_for_connection()
# Configure camera
detector.cam.acquire_time.put(0.1) # 100ms exposure
detector.cam.image_mode.put('Single')
# Stage detector for acquisition
detector.stage()
# Trigger acquisition
status = detector.trigger()
wait(status)
# Read image data
reading = detector.read()
# Unstage when done
detector.unstage()from ophyd.areadetector import AreaDetector, HDF5Plugin, TIFFPlugin
detector = AreaDetector('XF:28IDC:DET:', name='detector')
detector.wait_for_connection()
# Configure HDF5 plugin
hdf5 = detector.hdf5
hdf5.file_path.put('/data/detector/')
hdf5.file_name.put('scan')
hdf5.file_template.put('%s%s_%04d.h5')
hdf5.enable.put(1)
# Configure for 10 images
hdf5.num_capture.put(10)
detector.cam.num_images.put(10)
detector.cam.image_mode.put('Multiple')
# Start acquisition
detector.stage()
hdf5.capture.put(1) # Start file writing
status = detector.trigger()
wait(status) # Wait for all 10 images
detector.unstage()
print(f"Data saved to: {hdf5.full_file_name.get()}")detector = AreaDetector('XF:28IDC:DET:', name='detector')
detector.wait_for_connection()
# Enable statistics plugin
stats = detector.stats1
stats.enable.put(1)
# Configure region of interest for stats
stats.min_x.put(100)
stats.min_y.put(100)
stats.size_x.put(200)
stats.size_y.put(200)
# Take image and get statistics
detector.stage()
status = detector.trigger()
wait(status)
# Read statistics
print(f"Mean intensity: {stats.mean_value.get()}")
print(f"Max intensity: {stats.max_value.get()}")
print(f"Total counts: {stats.total.get()}")
detector.unstage()from ophyd.areadetector import AreaDetector, MultiTrigger
class MultiImageDetector(MultiTrigger, AreaDetector):
def __init__(self, *args, num_images=5, **kwargs):
super().__init__(*args, num_images=num_images, **kwargs)
detector = MultiImageDetector('XF:28IDC:DET:', name='detector', num_images=10)
detector.wait_for_connection()
# Configure exposure
detector.cam.acquire_time.put(0.05) # 50ms per image
# Stage for multi-image acquisition
detector.stage()
# Trigger acquisition of 10 images
status = detector.trigger()
wait(status) # Wait for all 10 images
detector.unstage()from ophyd import Component
from ophyd.areadetector import (AreaDetector, HDF5Plugin, TIFFPlugin,
StatsPlugin, ROIPlugin, ImagePlugin)
class MyAdvancedDetector(AreaDetector):
"""Custom detector with specific plugin configuration."""
# File plugins
hdf5 = Component(HDF5Plugin, 'HDF1:')
tiff = Component(TIFFPlugin, 'TIFF1:')
# Analysis plugins
stats1 = Component(StatsPlugin, 'Stats1:')
stats2 = Component(StatsPlugin, 'Stats2:')
roi1 = Component(ROIPlugin, 'ROI1:')
roi2 = Component(ROIPlugin, 'ROI2:')
# Display
image1 = Component(ImagePlugin, 'image1:')
detector = MyAdvancedDetector('XF:28IDC:DET:', name='detector')
detector.wait_for_connection()
# Configure multiple ROIs
detector.roi1.min_x.put(0)
detector.roi1.min_y.put(0)
detector.roi1.size_x.put(512)
detector.roi1.size_y.put(512)
detector.roi2.min_x.put(200)
detector.roi2.min_y.put(200)
detector.roi2.size_x.put(100)
detector.roi2.size_y.put(100)
# Connect stats plugins to ROIs
detector.stats1.nd_array_port.put('ROI1')
detector.stats2.nd_array_port.put('ROI2')
# Enable all plugins
for plugin in [detector.roi1, detector.roi2, detector.stats1, detector.stats2]:
plugin.enable.put(1)from ophyd.areadetector import SimDetector, SingleTrigger
class MySimDetector(SingleTrigger, SimDetector):
pass
sim_detector = MySimDetector('XF:28IDC:SIM:', name='sim_detector')
sim_detector.wait_for_connection()
# Configure simulation parameters
sim_detector.cam.sim_mode.put(1) # Linear ramp
sim_detector.cam.acquire_time.put(0.1)
# Take simulated image
sim_detector.stage()
status = sim_detector.trigger()
wait(status)
# The detector generates synthetic data
reading = sim_detector.read()
sim_detector.unstage()Install with Tessl CLI
npx tessl i tessl/pypi-ophyd