Skip to content
Open
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
14 changes: 14 additions & 0 deletions software/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,20 @@ Follow the instructions during the installation.

</details>

<details>
<summary>Installing SDK for VersaLase laser</summary>

Link: https://github.com/sthronevlt/VersaLSS

Run the following commands:
```
curl -L -O https://raw.githubusercontent.com/sthronevlt/VersaLSS/main/dist/laser_sdk-0.1.1-py3-none-any.whl
pip3 install laser_sdk-0.1.1-py3-none-any.whl
rm laser_sdk-0.1.1-py3-none-any.whl
```

</details>

## Configuring the software
Copy the .ini file associated with the microscope configuration to the software folder. Make modifications as needed (e.g. `camera_type`, `support_laser_autofocus`,`focus_camera_exposure_time_ms`)

Expand Down
33 changes: 23 additions & 10 deletions software/configurations/configuration_Squid+_Cicero_LDI.ini
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
[GENERAL]
camera_type = Hamamatsu
version = 2.0

camera_type = Toupcam
_camera_type_options = [Default, FLIR, Toupcam, Hamamatsu, Tucsen]
controller_sn = None
support_scimicroscopy_led_array = False
Expand All @@ -13,7 +15,7 @@ LDI_INTENSITY_MODE = "EXT"
LDI_SHUTTER_MODE = "EXT"
enable_spinning_disk_confocal = True
xlight_emission_filter_mapping = {405: 1, 470: 2, 555: 3, 640: 4, 730: 5}
xlight_serial_number = "AB0PKT2R"
xlight_serial_number = "B000DAO6"
xlight_sleep_time_for_wheel = 0.25
xlight_validate_wheel_pos = False
use_napari_for_live_view = False
Expand Down Expand Up @@ -134,11 +136,11 @@ wellplate_offset_y_mm = 0

focus_measure_operator = GLVA
controller_version = Teensy
support_laser_autofocus = True
support_laser_autofocus = False
focus_camera_type = Default
_focus_camera_type_options = [Default, FLIR, Toupcam]
_support_laser_autofocus_options = [True, False]
main_camera_model = C15440-20UP
main_camera_model = ITR3CMOS26000KMA
focus_camera_model = MER2-630-60U3M
focus_camera_exposure_time_ms = 0.8

Expand All @@ -157,7 +159,8 @@ default_multipoint_ny = 1
inverted_objective = True
_inverted_objective_options = [True, False]

filter_controller_enable = False
filter_controller_enable = Falsefilter_controller_enable = False

_filter_controller_enable_options = [True, False]

illumination_intensity_factor = 1
Expand All @@ -166,14 +169,24 @@ Z_MOTOR_CONFIG = "STEPPER"
_Z_MOTOR_CONFIG_OPTIONS = ["STEPPER", "STEPPER + PIEZO", "PIEZO", "LINEAR"]

[CAMERA_CONFIG]
roi_offset_x_default = 0
roi_offset_y_default = 0
roi_width_default = 6208
roi_height_default = 4168
rotate_image_angle = None
flip_image = None
_flip_image_options = [Vertical, Horizontal, Both, None]
crop_width_unbinned = 2304
crop_height_unbinned = 2304
binning_factor_default = 1
pixel_format_default = MONO16
_default_pixel_format_options = [MONO8, MONO16]
crop_width = 4168
crop_height = 4168
binning_factor_default = 2
pixel_format_default = MONO8
_default_pixel_format_options = [MONO8, MONO12, MONO14, MONO16, BAYER_RG8, BAYER_RG12]
temperature_default = 20
fan_speed_default = 1
blacklevel_value_default = 3
awb_ratios_r = 1.375
awb_ratios_g = 1
awb_ratios_b = 1.4141

[LIMIT_SWITCH_POLARITY]
x_home = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ trackers = ["csrt", "kcf", "mil", "tld", "medianflow", "mosse", "daSiamRPN"]
tracking_show_microscope_configurations = False
_tracking_show_microscope_configurations_options = [True, False]

tube_lens_mm = 180

wellplate_format = 384
_wellplate_format_options = [1536, 384, 96, 24, 12, 6]
x_mm_384_wellplate_upperleft = 12.41
Expand Down
10 changes: 6 additions & 4 deletions software/control/_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,13 +610,15 @@ class SOFTWARE_POS_LIMIT:
CAMERA_TYPE = "Default"
FOCUS_CAMERA_TYPE = "Default"

# Spinning disk confocal integration
ENABLE_SPINNING_DISK_CONFOCAL = False
USE_LDI_SERIAL_CONTROL = False
LDI_INTENSITY_MODE = "PC"
LDI_SHUTTER_MODE = "PC"
LDI_INTENSITY_MODE = "PC" # "PC" or "EXT"
LDI_SHUTTER_MODE = "PC" # "PC" or "EXT"
USE_CELESTA_ETHENET_CONTROL = False
USE_VORTRAN_LASER_USB_CONTROL = False
VORTRAN_SHUTTER_CONTROL_MODE = "PC" # "PC" or "EXT"

# Spinning disk confocal integration
ENABLE_SPINNING_DISK_CONFOCAL = False
XLIGHT_EMISSION_FILTER_MAPPING = {
405: 1,
470: 1,
Expand Down
2 changes: 1 addition & 1 deletion software/control/core/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -2151,7 +2151,7 @@ def get_acquisition_image_count(self):
try:
# We have Nt timepoints. For each timepoint, we capture images at all the regions. Each
# region has a list of coordinates that we capture at, and at each coordinate we need to
# do a capture for each requested camera + lighting + other configuration selected. So
# do a capture for each requested camera + illumination + other configuration selected. So
# total image count is:
coords_per_region = [
len(region_coords) for (region_id, region_coords) in self.scanCoordinates.region_fov_coordinates.items()
Expand Down
26 changes: 21 additions & 5 deletions software/control/gui_hcs.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# set QT_API environment variable
import os

import control.lighting
import control.illumination

os.environ["QT_API"] = "pyqt5"
import serial
Expand All @@ -23,7 +23,7 @@
import squid.config
import squid.stage.utils
import control.microscope
from control.lighting import LightSourceType, IntensityControlMode, ShutterControlMode, IlluminationController
from control.illumination import LightSourceType, IntensityControlMode, ShutterControlMode, IlluminationController
import squid.camera.utils

log = squid.logging.get_logger(__name__)
Expand Down Expand Up @@ -343,7 +343,7 @@ def loadSimulationObjects(self):

if USE_LDI_SERIAL_CONTROL:
self.ldi = serial_peripherals.LDI_Simulation()
self.illuminationController = control.lighting.IlluminationController(
self.illuminationController = control.illumination.IlluminationController(
self.microcontroller, self.ldi.intensity_mode, self.ldi.shutter_mode, LightSourceType.LDI, self.ldi
)
if USE_ZABER_EMISSION_FILTER_WHEEL:
Expand Down Expand Up @@ -428,9 +428,9 @@ def loadHardwareObjects(self):

if USE_CELESTA_ETHENET_CONTROL:
try:
import control.celesta
import control.illumination_celesta

self.celesta = control.celesta.CELESTA()
self.celesta = control.illumination_celesta.CELESTA()
self.illuminationController = IlluminationController(
self.microcontroller,
IntensityControlMode.Software,
Expand All @@ -442,6 +442,22 @@ def loadHardwareObjects(self):
self.log.error("Error initializing CELESTA")
raise

if USE_VORTRAN_LASER_USB_CONTROL:
try:
import control.illumination_versalase

self.versalase = control.illumination_versalase.VersaLase()
self.illuminationController = IlluminationController(
self.microcontroller,
IntensityControlMode.Software,
ShutterControlMode.TTL if VORTRAN_SHUTTER_CONTROL_MODE == "EXT" else ShutterControlMode.Software,
LightSourceType.VersaLase,
self.versalase,
)
except Exception:
self.log.error("Error initializing VersaLase")
raise

if USE_ZABER_EMISSION_FILTER_WHEEL:
try:
self.emission_filter_wheel = serial_peripherals.FilterController(
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import urllib.request
import traceback
from squid.abc import LightSource
from control.lighting import ShutterControlMode
from control.illumination import ShutterControlMode

import squid.logging

Expand Down
172 changes: 172 additions & 0 deletions software/control/illumination_versalase.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
from laser_sdk import LaserSDK

from squid.abc import LightSource
from control.illumination import ShutterControlMode, IntensityControlMode

import squid.logging


class VersaLase(LightSource):
def __init__(self, **kwds):
self._log = squid.logging.get_logger(__name__)

self.sdk = LaserSDK()
self.sdk.discover()

self.channel_mappings = {
405: None,
470: None,
488: None,
545: None,
550: None,
555: None,
561: None,
638: None,
640: None,
730: None,
735: None,
750: None,
}

try:
self.initialize()
except Exception as e:
self._log.error(f"Failed to initialize Vortran laser: {e}")

def initialize(self) -> bool:
"""
Initialize the connection and settings for the Vortran laser.
Returns True if successful, False otherwise.
"""
try:
# Query information about installed lasers
for laser in self.sdk.get_lasers():
self._log.info(f"Found laser {laser.wavelength}: {laser.max_power}")
laser.disable()
if laser.wavelength == 405:
self.channel_mappings[405] = laser.id
elif laser.wavelength == 488:
self.channel_mappings[470] = laser.id
self.channel_mappings[488] = laser.id
elif laser.wavelength == 545:
self.channel_mappings[545] = laser.id
self.channel_mappings[550] = laser.id
self.channel_mappings[555] = laser.id
self.channel_mappings[561] = laser.id
elif laser.wavelength == 638:
self.channel_mappings[638] = laser.id
self.channel_mappings[640] = laser.id
return True

except Exception as e:
self._log.error(f"Initialization failed: {e}")
return False

def set_intensity_control_mode(self, mode: IntensityControlMode):
"""
Set intensity control mode. Only software intensity control is supported for Vortran laser.

Args:
mode: IntensityControlMode.Software or IntensityControlMode.External
"""
self._log.debug("Only software intensity control is supported for Vortran laser")
pass

def get_intensity_control_mode(self) -> IntensityControlMode:
"""
Get current intensity control mode. Only software intensity control is supported for Vortran laser.

Returns:
IntensityControlMode enum value
"""
return IntensityControlMode.Software

def set_shutter_control_mode(self, mode: ShutterControlMode):
"""
Set shutter control mode for all lasers.

Args:
mode: ShutterControlMode enum
"""
for laser in self.sdk.get_lasers():
laser.set_digital_mode(mode == ShutterControlMode.TTL)

self.shutter_mode = mode

def get_shutter_control_mode(self) -> ShutterControlMode:
"""
Get current shutter control mode.

Returns:
ShutterControlMode enum value
"""
# The lasers in the VersaLase may have different shutter control states.
# We call set_shutter_control_mode() on initialize so they should all be the same.
# Raise an error here if they are not.
digital_mode = None
for laser in self.sdk.get_lasers():
if digital_mode is None:
digital_mode = laser.digital_mode
elif digital_mode != laser.digital_mode:
raise ValueError("Laser shutter control modes are not consistent")
if digital_mode is None:
raise ValueError("No lasers found")

return ShutterControlMode.TTL if digital_mode else ShutterControlMode.Software

def set_shutter_state(self, channel: int, state: bool):
"""
Turn a specific channel on or off.

Args:
channel: Channel ID (letter or wavelength)
state: True to turn on, False to turn off
"""
laser = self.sdk.get_laser_by_id(self.channel_mappings[channel])
laser.enable(state)

def get_shutter_state(self, channel: int) -> bool:
"""
Get the current shutter state of a specific channel.

Args:
channel: Channel ID (letter or wavelength)

Returns:
bool: True if channel is on, False if off
"""
laser = self.sdk.get_laser_by_id(self.channel_mappings[channel])
return laser.get_emission_status()

def set_intensity(self, channel: int, intensity: float):
"""
Set the intensity for a specific channel.

Args:
channel: Channel ID (letter or wavelength)
intensity: Intensity value (0-100 percent)
"""
laser = self.sdk.get_laser_by_id(self.channel_mappings[channel])
laser.set_power(laser.max_power * intensity / 100.0)

def get_intensity(self, channel: int) -> float:
"""
Get the current intensity of a specific channel.

Args:
channel: Channel ID (letter or wavelength)

Returns:
float: Current intensity value (0-100 percent)
"""
# For Vortran laser, we are able to get the actual intensity of the lasers.
# To keep consistency with other light sources, we return the set power/intensity here.
laser = self.sdk.get_laser_by_id(self.channel_mappings[channel])
laser_info = laser.get_op2()
return laser_info["LaserSetPower"] / laser.max_power * 100.0

def shut_down(self):
"""Safely shut down the Vortran laser."""
for laser in self.sdk.get_lasers():
laser.disable()
laser.disconnect()
6 changes: 3 additions & 3 deletions software/control/microscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
import squid.stage.utils

import control.microcontroller as microcontroller
from control.lighting import LightSourceType, IntensityControlMode, ShutterControlMode, IlluminationController
from control.illumination import LightSourceType, IntensityControlMode, ShutterControlMode, IlluminationController
from control.piezo import PiezoStage
import control.serial_peripherals as serial_peripherals
import control.filterwheel as filterwheel
Expand Down Expand Up @@ -260,9 +260,9 @@ def initialize_peripherals(self):

if USE_CELESTA_ETHENET_CONTROL:
try:
import control.celesta
import control.illumination_celesta

self.celesta = control.celesta.CELESTA()
self.celesta = control.illumination_celesta.CELESTA()
self.illuminationController = IlluminationController(
self.microcontroller,
IntensityControlMode.Software,
Expand Down
Loading