Skip to content

Modified for running in Linux and New stage driver #1045

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
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
439 changes: 145 additions & 294 deletions src/navigate/config/configuration.yaml

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions src/navigate/config/experiment.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
User:
name: Kevin_Dean
name: Steven_Sheppard
Saving:
root_directory: C:\Users\MicroscopyInnovation\Desktop\Data
save_directory: E://Kevin\Lung\MV3\GFP\2022-02-18\Cell000
user: Kevin
tissue: Lung
celltype: MV3
label: GFP
root_directory: ~/data/
save_directory: /mnt/data/
user: Steven
tissue: NA
celltype: NA
label: NA
file_type: TIFF
date: 2022-06-07
solvent: BABB
Expand Down
12 changes: 6 additions & 6 deletions src/navigate/config/gui_configuration.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
channel_settings:
count: 5
count: 4
laser_power:
step: 1
min: 0
max: 100
exposure_time:
step: 1
min: 1
max: 1000
max: 2000
interval:
step: 1
min: 1
max: 10
defocus:
step: 1
min: 0
min: -100
max: 100
stack_acquisition:
step_size:
Expand All @@ -30,9 +30,9 @@ stack_acquisition:
min: -10000
max: 10000
f_start_pos:
step: 0.01
min: -200
max: 2000
step: 1.0
min: -5000
max: 5000
f_end_pos:
step: 0.01
min: -200
Expand Down
1 change: 0 additions & 1 deletion src/navigate/controller/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.


# Standard Library Imports
from multiprocessing import Manager
import tkinter
Expand Down
35 changes: 27 additions & 8 deletions src/navigate/controller/sub_controllers/channels_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
# POSSIBILITY OF SUCH DAMAGE.
#

DEBUGGING = True

# Standard Library Imports
import logging
import datetime
Expand Down Expand Up @@ -195,12 +197,19 @@ def populate_experiment_values(self):
self.microscope_state_dict = self.parent_controller.configuration["experiment"][
"MicroscopeState"
]
# NOTE: If the step size is negative, this forces a positive step size.
# So if the z start/stop is decreasing it will not run as expected.
if self.microscope_state_dict["step_size"] < 0:
self.microscope_state_dict["step_size"] = -self.microscope_state_dict[
"step_size"
]
if DEBUGGING:
print('--ChannelsTab-- step size reversed')

if DEBUGGING:
print(f'--ChannelsTab-- stack acq vals:\n {self.stack_acq_vals}')

self.set_info(self.stack_acq_vals, self.microscope_state_dict)
# self.set_info(self.conpro_acq_vals, self.microscope_state_dict)
self.set_info(self.timepoint_vals, self.microscope_state_dict)

# check configuration for multiposition settings
Expand Down Expand Up @@ -229,6 +238,7 @@ def populate_experiment_values(self):
# after initialization
self.in_initialization = False
self.channel_setting_controller.in_initialization = False

# update z and f position
self.z_origin = self.parent_controller.configuration["experiment"][
"StageParameters"
Expand Down Expand Up @@ -375,6 +385,7 @@ def update_z_steps(self, *args):
# Calculate the number of slices and set GUI
try:
# validate the spinbox's value
# NOTE: start/end position are relative, not absolute
start_position = float(self.stack_acq_vals["start_position"].get())
end_position = float(self.stack_acq_vals["end_position"].get())
step_size = float(self.stack_acq_vals["step_size"].get())
Expand All @@ -391,11 +402,7 @@ def update_z_steps(self, *args):
except (KeyError, AttributeError):
logger.error("Error caught: updating z_steps")
return

# if step_size < 0.001:
# step_size = 0.001
# self.stack_acq_vals['step_size'].set(step_size)


number_z_steps = int(
np.ceil(np.abs((end_position - start_position) / step_size))
)
Expand All @@ -421,6 +428,14 @@ def update_z_steps(self, *args):
"abs_z_start"
].get()
self.microscope_state_dict["abs_z_end"] = self.stack_acq_vals["abs_z_end"].get()

if DEBUGGING:
print(
f"--ChannelsTab--\n",
f" f start: {self.stack_acq_vals['start_focus'].get()}\n",
f" f end: {self.stack_acq_vals['end_focus'].get()}\n",
f" f origin: {self.focus_origin}\n",
)
try:
self.microscope_state_dict["start_focus"] = self.stack_acq_vals[
"start_focus"
Expand Down Expand Up @@ -451,7 +466,7 @@ def update_start_position(self, *args):
Values is a dict as follows {'start_position': , 'abs_z_start': ,
'stack_z_origin': }
"""

# NOTE: pressing the set start/stop button sets the z/f origins. and sets the start and end positions to 0. So when setting up the zstack it should be done by first pressing start, then go to the end and press end?
# We have a new origin
self.z_origin = self.parent_controller.configuration["experiment"][
"StageParameters"
Expand Down Expand Up @@ -489,6 +504,7 @@ def update_end_position(self, *args):
"StageParameters"
]["f"]

# NOTE: Here we are setting the z/f start as the origin
z_start = self.z_origin
focus_start = self.focus_origin

Expand All @@ -497,6 +513,9 @@ def update_end_position(self, *args):
z_start, z_end = z_end, z_start
focus_start, focus_end = focus_end, focus_start

# NOTE: Now after making sure we are moving forward, we redefine the origin at the middle of the stack.
# This means when first setting start, then setting end, origin is going to be calculated as the middle between the 2,
# and the start positions are the old origins. This logic is why the z-stack setup is so specific.
# set origin to be in the middle of start and end
self.z_origin = (z_start + z_end) / 2
self.focus_origin = (focus_start + focus_end) / 2
Expand All @@ -507,6 +526,7 @@ def update_end_position(self, *args):
end_pos = z_end - self.z_origin
start_focus = focus_start - self.focus_origin
end_focus = focus_end - self.focus_origin
# NOTE: The parameters in the GUI are relative to origin which is 1/2 way between the start/end positions, when pressed
if flip_flags["z"]:
start_pos, end_pos = end_pos, start_pos
start_focus, end_focus = end_focus, start_focus
Expand Down Expand Up @@ -797,7 +817,6 @@ def update_experiment_values(self):
self.channel_setting_controller.update_experiment_values()
self.update_z_steps()


def verify_experiment_values(self):
"""Verify channel tab settings and return warning info

Expand Down
22 changes: 8 additions & 14 deletions src/navigate/controller/sub_controllers/tiling.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,26 +266,19 @@ def set_table(self):
y_stop = float(self.variables["y_end"].get())
y_tiles = int(self.variables["y_tiles"].get())

# NOTE: Removed shifting by the origin becuase, it was not clear how to set the origin.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should discuss this. I'm hesitant to make the change immediately, but we can consider a better way to communicate the origin.

# shift z by coordinate origin of local z-stack
z_start = float(self.variables["z_start"].get()) - float(
self.stack_acq_widgets["start_position"].get()
)
z_stop = float(self.variables["z_end"].get()) - float(
self.stack_acq_widgets["end_position"].get()
)
z_start = float(self.variables["z_start"].get()) # - float(self.stack_acq_widgets["start_position"].get())
z_stop = float(self.variables["z_end"].get()) # - float(self.stack_acq_widgets["end_position"].get())
z_tiles = int(self.variables["z_tiles"].get())

# Default to fixed theta
r_start = float(self.stage_position_vars["theta"].get())
r_stop = float(self.stage_position_vars["theta"].get())
r_tiles = 1

f_start = float(self.variables["f_start"].get()) - float(
self.stack_acq_widgets["start_focus"].get()
)
f_stop = float(self.variables["f_end"].get()) - float(
self.stack_acq_widgets["end_focus"].get()
)
f_start = float(self.variables["f_start"].get()) #- float(self.stack_acq_widgets["start_focus"].get())
f_stop = float(self.variables["f_end"].get()) #- float(self.stack_acq_widgets["end_focus"].get())
f_tiles = int(self.variables["f_tiles"].get())

# for consistency, always go from low to high
Expand All @@ -308,11 +301,12 @@ def sort_vars(a, b):
return b, a
return a, b

#NOTE: Sorting variables breaks down if the F and Z stage are not both moving in the same direction. On our microscope, the F stage moves in a negative direction for positive z-stack acquisitions. I also have a hard coded (-) in commmon_features.py to allow the focus stage to move in a negative direction.
x_start, x_stop = sort_vars(x_start, x_stop)
y_start, y_stop = sort_vars(y_start, y_stop)
z_start, z_stop = sort_vars(z_start, z_stop)
r_start, r_stop = sort_vars(r_start, r_stop)
f_start, f_stop = sort_vars(f_start, f_stop)
# f_start, f_stop = sort_vars(f_start, f_stop)

overlap = float(self._percent_overlap) / 100
table_values = compute_tiles_from_bounding_box(
Expand Down
31 changes: 18 additions & 13 deletions src/navigate/model/device_startup_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,6 @@ def load_stages(
stage_devices = []

stages = configuration["configuration"]["hardware"]["stage"]

if type(stages) != ListProxy:
stages = [stages]

Expand All @@ -461,7 +460,6 @@ def load_stages(

else:
stage_type = stage_config["type"]

if stage_type == "PI" and platform.system() == "Windows":
from navigate.model.devices.stages.pi import build_PIStage_connection
from pipython.pidevice.gcserror import GCSError
Expand Down Expand Up @@ -511,8 +509,18 @@ def load_stages(
exception=TLFTDICommunicationError,
)
)

elif stage_type == "KST101":
elif stage_type == "KINESIS" and platform.system() == "Linux":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now obsolete with the latest PR.

from navigate.model.devices.stages.tl_kinesis_steppermotor import (
build_KINESIS_Stage_connection
)
stage_devices.append(
auto_redial(
build_KINESIS_Stage_connection,
(stage_config["serial_number"],),
exception=Exception
)
)
elif stage_type == "KST101" and platform.system=="Windows":
from navigate.model.devices.stages.tl_kcube_steppermotor import (
build_TLKSTStage_connection,
)
Expand Down Expand Up @@ -562,12 +570,8 @@ def load_stages(
)
)

elif stage_type == "MS2000" and platform.system() == "Windows":
"""Filter wheel can be controlled from the same Controller. If
so, then we will load this as a shared device. If not, we will create the
connection to the Controller.

TODO: Evaluate whether MS2000 should be able to operate as a shared device.
elif stage_type == "MS2000":
"""Stage and filter wheel are independent and should not be a shared device
"""

from navigate.model.devices.stages.asi_MSTwoThousand import (
Expand Down Expand Up @@ -705,7 +709,10 @@ def start_stage(
from navigate.model.devices.stages.tl_kcube_inertial import TLKIMStage

return TLKIMStage(microscope_name, device_connection, configuration, id)

elif device_type == "KINESIS":
Copy link
Collaborator

@AdvancedImagingUTSW AdvancedImagingUTSW Feb 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This device should also be added to the configuration wizard.

from navigate.model.devices.stages.tl_kinesis_steppermotor import TLKINStage

return TLKINStage(microscope_name, device_connection, configuration, id)
elif device_type == "KST101":
from navigate.model.devices.stages.tl_kcube_steppermotor import TLKSTStage

Expand Down Expand Up @@ -1265,7 +1272,6 @@ def start_lasers(
modulation = "analog"
elif digital == "NI":
modulation = "digital"

return LaserNI(
microscope_name=microscope_name,
device_connection=device_connection,
Expand Down Expand Up @@ -1416,7 +1422,6 @@ def start_galvo(
Galvo : GalvoBase
Galvo scanning class.
"""

if plugin_devices is None:
plugin_devices = {}

Expand Down
13 changes: 8 additions & 5 deletions src/navigate/model/devices/APIs/asi/asi_MS2000_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import threading
import time
import logging
import platform

# Third Party Imports
from serial import Serial
Expand Down Expand Up @@ -79,6 +80,7 @@ def __init__(self, code: str):
":N-6": "Undefined Error (command is incorrect, but the controller does "
"not know exactly why.",
":N-21": "Serial Command halted by the HALT command",
":N-21\r\n": "Serial Command halted by the HALT command",
}
#: str: Error code received from MS2000 Console
self.code = code
Expand Down Expand Up @@ -191,8 +193,9 @@ def connect_to_serial(
self.serial_port.write_timeout = write_timeout
self.serial_port.timeout = read_timeout

# set the size of the rx and tx buffers before calling open
self.serial_port.set_buffer_size(rx_size, tx_size)
if platform.system()=="Windows":
# Only changed the buffer size in windows
self.serial_port.set_buffer_size(rx_size, tx_size)
try:
self.serial_port.open()
except SerialException:
Expand All @@ -216,8 +219,8 @@ def connect_to_serial(
"X",
"Y",
"Z",
] # self.get_default_motor_axis_sequence()

]
def get_default_motor_axis_sequence(self) -> None:
"""Get the default motor axis sequence from the ASI device

Expand Down Expand Up @@ -374,7 +377,7 @@ def read_response(self) -> str:
self.report_to_console(f"Received Response: {response.strip()}")
if response.startswith(":N"):
logger.error(f"Incorrect response received: {response}")
raise MS2000Exception(response)
raise MS2000Exception(response.strip())

return response # in case we want to read the response

Expand Down
Loading