Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/api/pylabrobot.agilent.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ BioTek EL406
BioTek Cytation
---------------

.. currentmodule:: pylabrobot.agilent.biotek.cytation
.. currentmodule:: pylabrobot.agilent.biotek.plate_readers.cytation

.. autosummary::
:toctree: _autosummary
Expand All @@ -83,10 +83,10 @@ BioTek Cytation

Cytation1
Cytation5
CytationBackend
CytationMicroscopyBackend
CytationImagingConfig

.. autoclass:: pylabrobot.agilent.biotek.cytation.CytationBackend.CaptureParams
.. autoclass:: pylabrobot.agilent.biotek.plate_readers.cytation.microscopy_backend.CytationMicroscopyBackend.CaptureParams
:members:

.. currentmodule:: pylabrobot.agilent.biotek.loading_tray_backend
Expand All @@ -108,7 +108,7 @@ BioTek Cytation
BioTek Synergy H1
------------------

.. currentmodule:: pylabrobot.agilent.biotek.synergy_h1
.. currentmodule:: pylabrobot.agilent.biotek.plate_readers.synergy

.. autosummary::
:toctree: _autosummary
Expand All @@ -118,7 +118,7 @@ BioTek Synergy H1
SynergyH1
SynergyH1Backend

.. autoclass:: pylabrobot.agilent.biotek.biotek.BioTekBackend.LuminescenceParams
.. autoclass:: pylabrobot.agilent.biotek.plate_readers.base.BioTekBackend.LuminescenceParams
:members:


Expand Down
7 changes: 4 additions & 3 deletions docs/user_guide/_getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,9 @@ If you are still having trouble, please reach out on [discuss.pylabrobot.org](ht

In order to use imaging on the Cytation, you need to:

1. Install python 3.10
2. Download Spinnaker SDK and install (including Python) [https://www.teledynevisionsolutions.com/products/spinnaker-sdk/](https://www.teledynevisionsolutions.com/products/spinnaker-sdk/)
3. Install the cytation-microscopy dependencies: `pip install "pylabrobot[cytation-microscopy]"`
1. Install the Aravis system library:
- macOS: `brew install aravis`
- Linux: `sudo apt-get install libaravis-dev gobject-introspection`
2. Install the cytation-microscopy dependencies: `pip install "pylabrobot[cytation-microscopy]"` (this pulls in PyGObject, numpy, and opencv-python)

If you just want to do plate reading, heating, shaking, etc. you don't need to follow these specific steps.
250 changes: 207 additions & 43 deletions docs/user_guide/agilent/biotek/cytation/hello-world.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -3,135 +3,299 @@
{
"cell_type": "markdown",
"id": "wez9bxdabm",
"source": "# Agilent BioTek Cytation\n\nThe Cytation is an Agilent BioTek multi-mode plate reader with optional microscopy imaging. Depending on the model it supports:\n\n- [Absorbance](../../../capabilities/absorbance)\n- [Fluorescence](../../../capabilities/fluorescence)\n- [Luminescence](../../../capabilities/luminescence)\n- [Microscopy](../../../capabilities/microscopy)\n- [Temperature control](../../../capabilities/temperature-control)\n\n| Model | PLR Name | Plate Reading | Microscopy | Temperature |\n|---|---|---|---|---|\n| Cytation 5 | `Cytation5` | Absorbance, Fluorescence, Luminescence | yes | yes |\n| Cytation 1 | `Cytation1` | -- | yes | yes |\n\nBoth models share the `CytationBackend` driver, which communicates over FTDI USB. The Cytation 5 adds plate-reading capabilities on top of the shared microscopy and temperature-control features.",
"metadata": {}
"metadata": {},
"source": [
"# Agilent BioTek Cytation\n",
"\n",
"The Cytation is an Agilent BioTek multi-mode plate reader with optional microscopy imaging. Depending on the model it supports:\n",
"\n",
"- [Absorbance](../../../capabilities/absorbance)\n",
"- [Fluorescence](../../../capabilities/fluorescence)\n",
"- [Luminescence](../../../capabilities/luminescence)\n",
"- [Microscopy](../../../capabilities/microscopy)\n",
"- [Temperature control](../../../capabilities/temperature-control)\n",
"\n",
"| Model | PLR Name | Plate Reading | Microscopy | Temperature |\n",
"|---|---|---|---|---|\n",
"| Cytation 5 | `Cytation5` | Absorbance, Fluorescence, Luminescence | yes | yes |\n",
"| Cytation 1 | `Cytation1` | -- | yes | yes |\n",
"\n",
"Both models use `BioTekBackend` for serial communication and `CytationMicroscopyBackend` for imaging, communicating over FTDI USB. The Cytation 5 adds plate-reading capabilities on top of the shared microscopy and temperature-control features."
]
},
{
"cell_type": "markdown",
"id": "0rn94ubvq8dj",
"source": "## Setup\n\nThe examples below use a Cytation 5. For a Cytation 1, replace `Cytation5` with `Cytation1` (the Cytation 1 does not have `.absorbance`, `.fluorescence`, or `.luminescence` attributes).",
"metadata": {}
"metadata": {},
"source": [
"## Setup\n",
"\n",
"The examples below use a Cytation 5. For a Cytation 1, replace `Cytation5` with `Cytation1` (the Cytation 1 does not have `.absorbance`, `.fluorescence`, or `.luminescence` attributes)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ia4t5ga2ldg",
"source": "from pylabrobot.agilent.biotek.cytation import Cytation5\n\nc5 = Cytation5(name=\"cytation5\")\nawait c5.setup()",
"metadata": {},
"execution_count": null,
"outputs": []
"outputs": [],
"source": [
"from pylabrobot.agilent.biotek import Cytation5\n",
"\n",
"c5 = Cytation5(name=\"cytation5\", device_id=\"20060813\")\n",
"await c5.setup()"
]
},
{
"cell_type": "markdown",
"id": "35rmpdivj44",
"source": "Open and close the loading tray. Pass `BioTekLoadingTrayBackend.OpenParams(slow=True)` or `CloseParams(slow=True)` for slower motor travel if needed.",
"metadata": {}
"metadata": {},
"source": [
"Open and close the loading tray. Pass `BioTekLoadingTrayBackend.OpenParams(slow=True)` or `CloseParams(slow=True)` for slower motor travel if needed."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "l85qt1z6hdf",
"source": "await c5.loading_tray.open()\n\nfrom pylabrobot.resources import Cor_96_wellplate_360ul_Fb\nplate = Cor_96_wellplate_360ul_Fb(name=\"plate\")\nc5.loading_tray.assign_child_resource(plate)\n\nawait c5.loading_tray.close()",
"metadata": {},
"outputs": [],
"source": [
"await c5.loading_tray.open()\n",
"\n",
"from pylabrobot.resources import Cor_96_wellplate_360ul_Fb\n",
"plate = Cor_96_wellplate_360ul_Fb(name=\"plate\", with_lid=True)\n",
"c5.loading_tray.assign_child_resource(plate)"
]
},
{
"cell_type": "code",
"execution_count": null,
"outputs": []
"id": "76ae5e62",
"metadata": {},
"outputs": [],
"source": [
"await c5.loading_tray.close()"
]
},
{
"cell_type": "markdown",
"id": "hxkf2luxk9n",
"source": "## Plate reading (Cytation 5 only)\n\nThe Cytation 5 exposes `.absorbance`, `.fluorescence`, and `.luminescence` capability objects. For the full API, see [Absorbance](../../../capabilities/absorbance), [Fluorescence](../../../capabilities/fluorescence), and [Luminescence](../../../capabilities/luminescence).",
"metadata": {}
"metadata": {},
"source": [
"## Plate reading (Cytation 5 only)\n",
"\n",
"The Cytation 5 exposes `.absorbance`, `.fluorescence`, and `.luminescence` capability objects. For the full API, see [Absorbance](../../../capabilities/absorbance), [Fluorescence](../../../capabilities/fluorescence), and [Luminescence](../../../capabilities/luminescence)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "hwipa2rwkzl",
"source": "# Absorbance\ndata = await c5.absorbance.read_absorbance(wavelength=450)",
"metadata": {},
"execution_count": null,
"outputs": []
"outputs": [],
"source": [
"# Absorbance\n",
"data = await c5.absorbance.read_absorbance(wavelength=450)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "jmvn8du2t5",
"source": "# Fluorescence\ndata = await c5.fluorescence.read_fluorescence(\n excitation_wavelength=485, emission_wavelength=528, focal_height=7.5\n)",
"metadata": {},
"execution_count": null,
"outputs": []
"outputs": [],
"source": [
"# Fluorescence\n",
"data = await c5.fluorescence.read_fluorescence(\n",
" excitation_wavelength=485, emission_wavelength=528, focal_height=7.5\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "oxn123gjoh",
"source": "# Luminescence\ndata = await c5.luminescence.read_luminescence(focal_height=4.5)",
"metadata": {},
"execution_count": null,
"outputs": []
"outputs": [],
"source": [
"# Luminescence\n",
"data = await c5.luminescence.read_luminescence(focal_height=4.5)"
]
},
{
"cell_type": "markdown",
"id": "1qn3t2pqvw",
"source": "## Microscopy\n\nBoth the Cytation 5 and Cytation 1 expose a `.microscope` capability. For imaging, pass `use_cam=True` during setup so the Spinnaker camera is initialized. For the full API, see [Microscopy](../../../capabilities/microscopy).\n\nUse {class}`~pylabrobot.agilent.biotek.cytation.CytationBackend.CaptureParams` to control LED intensity, coverage tiling, and pixel format.",
"metadata": {}
"metadata": {},
"source": [
"## Microscopy\n",
"\n",
"Both the Cytation 5 and Cytation 1 expose a `.microscopy` capability. The Aravis camera is initialized during setup. For the full API, see [Microscopy](../../../capabilities/microscopy).\n",
"\n",
"Use {class}`~pylabrobot.agilent.biotek.plate_readers.cytation.microscopy_backend.CytationMicroscopyBackend.CaptureParams` to control LED intensity and coverage tiling."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "qr1jm6691a",
"source": "from pylabrobot.agilent.biotek.cytation import CytationBackend, CytationImagingConfig\nfrom pylabrobot.capabilities.microscopy.standard import ImagingMode, Objective\n\nres = await c5.microscope.capture(\n row=1,\n column=2,\n mode=ImagingMode.BRIGHTFIELD,\n objective=Objective.O_4X_PL_FL_Phase,\n focal_height=0.833,\n exposure_time=5,\n gain=16,\n plate=plate,\n backend_params=CytationBackend.CaptureParams(led_intensity=10),\n)",
"metadata": {},
"outputs": [],
"source": [
"from pylabrobot.agilent.biotek import CytationMicroscopyBackend\n",
"from pylabrobot.capabilities.microscopy.standard import ImagingMode, Objective\n",
"\n",
"res = await c5.microscopy.capture(\n",
" well=(1, 2),\n",
" mode=ImagingMode.BRIGHTFIELD,\n",
" objective=Objective.O_4X_PL_FL_Phase,\n",
" focal_height=1.833,\n",
" exposure_time=5,\n",
" gain=8,\n",
" plate=plate,\n",
" backend_params=CytationMicroscopyBackend.CaptureParams(led_intensity=10),\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"outputs": []
"id": "afb1bd4d",
"metadata": {},
"outputs": [],
"source": [
"from PIL import Image\n",
"Image.fromarray(res.images[0])"
]
},
{
"cell_type": "markdown",
"id": "km95iou30f",
"source": "Tile multiple fields of view with the `coverage` parameter:",
"metadata": {}
"metadata": {},
"source": [
"Tile multiple fields of view with the `coverage` parameter:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fi1o94l1uni",
"source": "res = await c5.microscope.capture(\n row=1,\n column=2,\n mode=ImagingMode.BRIGHTFIELD,\n objective=Objective.O_4X_PL_FL_Phase,\n focal_height=0.833,\n exposure_time=5,\n gain=16,\n plate=plate,\n backend_params=CytationBackend.CaptureParams(\n led_intensity=10,\n coverage=(4, 4),\n ),\n)\nprint(f\"{len(res.images)} images captured\")",
"metadata": {},
"outputs": [],
"source": [
"res = await c5.microscopy.capture(\n",
" well=(1, 2),\n",
" mode=ImagingMode.BRIGHTFIELD,\n",
" objective=Objective.O_4X_PL_FL_Phase,\n",
" focal_height=2,\n",
" exposure_time=5,\n",
" gain=8,\n",
" plate=plate,\n",
" backend_params=CytationMicroscopyBackend.CaptureParams(\n",
" led_intensity=10,\n",
" coverage=(1, 1),\n",
" ),\n",
")\n",
"print(f\"{len(res.images)} images captured\")\n",
"\n",
"from PIL import Image\n",
"Image.fromarray(res.images[0])"
]
},
{
"cell_type": "code",
"execution_count": null,
"outputs": []
"id": "de5018a8",
"metadata": {},
"outputs": [],
"source": [
"# Autofocus\n",
"from pylabrobot.capabilities.microscopy import AutoFocus, evaluate_focus_nvmg_sobel\n",
"\n",
"res = await c5.microscopy.capture(\n",
" well=(1, 2),\n",
" mode=ImagingMode.BRIGHTFIELD,\n",
" objective=Objective.O_4X_PL_FL_Phase,\n",
" focal_height=AutoFocus(\n",
" evaluate_focus=evaluate_focus_nvmg_sobel,\n",
" timeout=60,\n",
" low=1,\n",
" high=3,\n",
" ),\n",
" exposure_time=5,\n",
" gain=8,\n",
" plate=plate,\n",
" backend_params=CytationMicroscopyBackend.CaptureParams(led_intensity=10),\n",
")\n",
"print(f\"best focal height: {res.focal_height:.3f} mm\")\n",
"\n",
"from PIL import Image\n",
"Image.fromarray(res.images[0])"
]
},
{
"cell_type": "markdown",
"id": "sa9pdeeo51",
"source": "## Temperature control\n\nBoth models expose a `.temperature` controller. For the full API, see [Temperature Control](../../../capabilities/temperature-control).",
"metadata": {}
"metadata": {},
"source": [
"## Temperature control\n",
"\n",
"Both models expose a `.temperature` controller. For the full API, see [Temperature Control](../../../capabilities/temperature-control)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "qhsjnerhl3",
"source": "await c5.temperature.set_temperature(37.0)\n\ncurrent = await c5.temperature.request_temperature()\nprint(f\"{current:.1f} \\u00b0C\")\n\nawait c5.temperature.deactivate()",
"metadata": {},
"execution_count": null,
"outputs": []
"outputs": [],
"source": [
"await c5.temperature.set_temperature(37.0)\n",
"\n",
"current = await c5.temperature.request_temperature()\n",
"print(f\"{current:.1f} \\u00b0C\")\n",
"\n",
"await c5.temperature.deactivate()"
]
},
{
"cell_type": "markdown",
"id": "f667qnt4occ",
"source": "## Teardown",
"metadata": {}
"metadata": {},
"source": [
"## Teardown"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "xcqz2zwu04g",
"source": "await c5.stop()",
"metadata": {},
"execution_count": null,
"outputs": []
"outputs": [],
"source": [
"await c5.stop()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"display_name": "env",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"version": "3.11.0"
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.14.3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
}
Loading
Loading