Skip to content
8 changes: 7 additions & 1 deletion software/control/core/multi_point_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,13 @@ def set_use_piezo(self, checked):
def set_z_stacking_config(self, z_stacking_config_index):
if z_stacking_config_index in control._def.Z_STACKING_CONFIG_MAP:
self.z_stacking_config = control._def.Z_STACKING_CONFIG_MAP[z_stacking_config_index]
print(f"z-stacking configuration set to {self.z_stacking_config}")
self._log.debug(f"z-stacking configuration set to {self.z_stacking_config}")
else:
self._log.warning(
f"Invalid z_stacking_config_index: {z_stacking_config_index}. "
f"Valid indices: {list(control._def.Z_STACKING_CONFIG_MAP.keys())}. "
f"Keeping current config: {self.z_stacking_config}"
)

def set_z_range(self, minZ, maxZ):
self.z_range = [minZ, maxZ]
Expand Down
16 changes: 14 additions & 2 deletions software/control/core/multi_point_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -635,11 +635,23 @@ def run_single_time_point(self):
finally:
self.microcontroller.enable_joystick(True)

_VALID_Z_STACKING_CONFIGS = set(Z_STACKING_CONFIG_MAP.values())

def initialize_z_stack(self):
# z stacking config
if self.z_stacking_config not in self._VALID_Z_STACKING_CONFIGS:
self._log.error(
f"Invalid z_stacking_config: '{self.z_stacking_config}'. "
f"Valid values: {self._VALID_Z_STACKING_CONFIGS}. Defaulting to 'FROM BOTTOM'."
)
self.z_stacking_config = "FROM BOTTOM"

if self.z_stacking_config == "FROM TOP":
self.deltaZ = -abs(self.deltaZ)
self.move_to_z_level(self.z_range[1])
elif self.z_stacking_config == "FROM CENTER":
# Move to center of z-range so autofocus runs at the correct Z level
center_z = (self.z_range[0] + self.z_range[1]) / 2
self.move_to_z_level(center_z)
else:
self.move_to_z_level(self.z_range[0])

Expand Down Expand Up @@ -1282,7 +1294,7 @@ def perform_autofocus(self, region_id, fov):
return True

def prepare_z_stack(self):
Copy link

Copilot AI Jan 28, 2026

Choose a reason for hiding this comment

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

prepare_z_stack() no longer repositions the stage for FROM CENTER, but perform_autofocus() still runs before prepare_z_stack() and assumes the current Z is the stack center, and move_z_back_after_stack() still contains FROM CENTER-specific logic. With the new behavior, subsequent FOVs will start imaging from the AF plane (center) instead of the stack start, and the first FOV may autofocus at the stack start (z_range[0]) rather than the center. Either restore a FROM CENTER reposition here (preferably moving to z_range[0] relative to the post-AF center), or update the worker to consistently use z_range/midpoint for AF and stack start/end across FOVs.

Suggested change
def prepare_z_stack(self):
def prepare_z_stack(self):
# For FROM CENTER stacking, autofocus leaves us at the stack center.
# Move to the start of the z range (relative to this center) before imaging.
if getattr(self, "NZ", 1) > 1 and getattr(self, "z_stacking_config", None) == "FROM CENTER":
try:
start_offset_um = self.z_range[0]
except (AttributeError, IndexError, TypeError):
start_offset_um = None
if start_offset_um is not None:
self._log.info(
"Preparing z stack (FROM CENTER): moving to start offset %.3f µm from AF plane",
start_offset_um,
)
# z_range values are in µm; convert to mm for the stage.move_z API.
self.stage.move_z(start_offset_um / 1000.0)
self.wait_till_operation_is_completed()

Copilot uses AI. Check for mistakes.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

[Claude Code] Fixed in commit 03f9890 - Restored FROM CENTER logic: initialize_z_stack() now moves to center (midpoint of z_range) so AF runs at correct Z level, and prepare_z_stack() moves from center to bottom before imaging. move_z_back_after_stack() was already correct.

# move to bottom of the z stack
# For FROM CENTER: autofocus ran at center, now move to bottom before imaging
if self.z_stacking_config == "FROM CENTER":
self.stage.move_z(-self.deltaZ * round((self.NZ - 1) / 2.0))
self._sleep(SCAN_STABILIZATION_TIME_MS_Z / 1000)
Expand Down
Loading