redsun-mimir
redsun components for Mimir,
a portable interferometric scattering microscope
(iSCAT) in development, with a hardware controller by openUC2.
Each piece of hardware runs in its own process, as a redsun service served
over PVAccess by fastcs:
pymmcore-plus drives the
camera and stages, oxiserial the
openUC2 board.
Install
In a virtual environment:
uv venv --python 3.11 # or: python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
uv pip install redsun-mimir # or: pip install redsun-mimir
From source, with uv:
git clone https://github.com/redsun-acquisition/redsun-mimir
cd redsun-mimir
uv sync
Run the simulation
uv pip install "redsun-mimir[sim]" # or pip
mmcore install --test-adapters # Micro-Manager demo adapters
mimir sim
Other ways to get the adapters are in the
pymmcore-plus docs.
mimir uc2 runs the session for the real board.
mimir sim running live_stream on the Micro-Manager
demo camera: acquisition controls on the left, the napari viewer in the
centre, detector, light and motor controls on the right.
Services
A session starts and stops each service; its device reaches the service over PVAccess at the service's prefix.
| service | owns | arguments |
|---|---|---|
mmcore-camera |
one Micro-Manager camera | --adapter, --device, --properties |
mmcore-stage |
one Micro-Manager stage | --adapter, --device, --axes |
youseetoo-controller |
the openUC2 board's serial port | --port, --baudrate |
properties lists, comma-separated, the camera properties to publish
besides exposure and roi; none by default.
services:
transport: pv-access
camera1_ioc:
plugin_name: redsun-mimir
plugin_id: mmcore-camera
prefix: "MIMIR-CAM1:"
args:
adapter: DemoCamera
device: DCam
properties: Binning
storage:
base_dir: "D:/mimir-data" # optional; the user data directory otherwise
A device names its service, so the prefix is written once:
mmcamera: Annotated[AsDevice[MMCamera], Declare(service="camera1_ioc")]
Session classes
MimirApp, a QtSession, declares the presenters, views and napari hook and
links them in wire(). The two examples subclass it with their devices and
session file:
from pathlib import Path
from typing import Annotated
from redsun import AsDevice, Declare
from redsun_mimir.device import MockLightDevice
from redsun_mimir.device.mmcore import MMCamera, MMStage
class MimirSimulator(MimirApp):
config = Path(__file__).parent / "full_configuration.yaml"
mmcamera: Annotated[AsDevice[MMCamera], Declare(service="camera1_ioc")]
XY: Annotated[AsDevice[MMStage], Declare(service="xy_stage")]
laser: AsDevice[MockLightDevice]
The session reads these annotations at run time, so import the classes they
name normally, not under if TYPE_CHECKING:.
Components never name each other's classes:
- A view asks in
setupfor a protocol fromredsun_mimir.protocols:DetectorViewandImageViewforDescribesDetectors,MotorViewforDescribesMotors,LightViewforDescribesLights.DetectorPresenterasks forHoldsDeferrals, whichAcquisitionPresentersatisfies. - A presenter asks for
DevicesOf[P]and gets only the devices satisfyingP, such asDetectorProtocolforDetectorPresenter. AcquisitionPresenterofferslive_stream,MedianPresenterofferslive_median_scan, andAcquisitionPresenterruns both.
The napari hook
ImageView embeds a napari viewer and takes its stylesheet from the
application. NapariApplication serves two hook points: it creates the
application napari would build (create_application) and puts napari's
stylesheet on it (configure_application). The shipped sessions declare it.
A session built only from a file needs a hooks: section, one provider for
both points:
hooks:
create_application: &napari
provider: "redsun_mimir.hooks:NapariApplication"
configure_application: *napari
Without it the viewer works and its canvas follows napari's theme, but the layer list, layer controls and other Qt widgets keep the default look.
Wiring a session from YAML
The session classes make their links in wire(). A session built only from a
file lists them under wiring:, each signal mapped to one slot or a list.
Without it the components link to nothing. Never add it to a file that backs a
session class with wire(): every link would be made twice.
wiring:
det_ctrl.sig_new_data: img_widget.update_layers
median_ctrl.median: img_widget.update_layers
median_ctrl.filtered: img_widget.update_layers
det_widget.sig_property_changed: det_ctrl.set
det_ctrl.sig_new_configuration:
- det_widget.on_new_configuration
- img_widget.on_new_configuration
img_widget.sig_roi_drawn: det_widget.on_roi_drawn
det_widget.sig_roi_selection: img_widget.set_roi_selection
det_widget.sig_roi_edited: img_widget.set_roi_box
motor_widget.sig_motor_move: motor_ctrl.move
light_widget.sig_toggle_light_request: light_ctrl.trigger
light_widget.sig_intensity_request: light_ctrl.set
acq_widget.sig_launch_plan_request: acq_ctrl.launch_plan
acq_widget.sig_stop_plan_request: acq_ctrl.stop_plan
acq_widget.sig_pause_resume_request: acq_ctrl.pause_or_resume_plan
acq_widget.sig_action_request: acq_ctrl.request_action
acq_ctrl.sig_progress: acq_widget.on_progress
acq_ctrl.sig_plan_done:
- acq_widget.on_plan_done
- path_provider.reset_plan
acq_ctrl.sig_pre_launch_notify:
- median_ctrl.clear_medians
- path_provider.set_plan
acq_widget.sig_base_dir_request: path_provider.set_base_dir
path_provider.sig_base_dir_changed: acq_widget.on_base_dir_changed
acq_ctrl.sig_locks_changed:
- det_widget.set_locked
- motor_widget.set_locked
- light_widget.set_locked
Names are the keys under devices:, presenters: and views:, then the
signal or slot. path_provider is the session's path provider: it names the
directory a capture goes to and refuses a new one while a plan runs.
acq_ctrl.request_action passes an action request to the component offering
the running plan.
A file can only address component.port, so two links need a session class:
acq_ctrl.actions.sig_changedandmedian_ctrl.actions.sig_changedtoacq_widget.on_action_changed; without it the action buttons stay disabled.- each motor axis readback to
motor_widget.update_setpoint; without it the position labels do not move.
Features
- Live acquisition, shown with
napari. - Region of interest: Select ROI opens an editor and a box over the detector's layer. Drag the box or type the numbers; Full selects the whole sensor, OK applies. A change asked for during a plan lands between two of its messages.
- Background removal by the median of a square scan, following this
paper:
MedianPresenterofferslive_median_scan. The scan's frames are written beside the next capture as<detector>_scan; the median stays in memory. - Progress bars on the acquisition page, for a scan and for a capture of a set number of frames or of frames until stopped.
- Each capture and scan is a run of its own, nested in the live run; its start document names the run it serves and the scan it follows.
- A run gets the document callbacks its plan lists plus those the user leaves ticked, and no others.
- Zarr v3 storage with
acquire-zarr, written by the camera's service. - Manual control of light sources and motors.
- The session log in its own window, from View > Logs.
- Extensible with further
redsuncomponents.
Contributing
Contributions are welcome. Run the tests with pytest and keep coverage at least where it is.
License
Apache Software License 2.0.
redsun-mimir is free and open source software.
Issues
File an issue with a detailed description.
Metadata
Release files for redsun-mimir 0.5.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| redsun_mimir-0.5.1.tar.gz | 74.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| redsun_mimir-0.5.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 171.7 kB
Release files / redsun_mimir-0.5.1.tar.gz
| Download URL | redsun_mimir-0.5.1.tar.gz |
|---|---|
| Size | 74.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cb684fec1372effc405ee5c835b0536510bcb4cdf05592337fcbb5066dcb64ba
|
|
BLAKE2b-256 checksum How to use checksums |
9dd44c6e72a38ccf1b800fac5ecf4fd4ec178efa8a6d172b0a2de5ea64effb3b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency logRelease files / redsun_mimir-0.5.1-py3-none-any.whl
| Download URL | redsun_mimir-0.5.1-py3-none-any.whl |
|---|---|
| Size | 97.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5d2c6168f26f16f13c918e45ab661194e13fb6887ef97315ec432b8a20ac2611
|
|
BLAKE2b-256 checksum How to use checksums |
285fac23fd4a68ce862fe48db31a38e4b6bafd3103797f696bdd2873c4cdd6dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 1, 2026.
Transparency log