Spelling corrections and docstring improvements

Co-authored by Joe Knapper <jaknapper@hotmail.com>
This commit is contained in:
Julian Stirling 2025-04-14 15:46:27 +00:00
parent bd411db5ea
commit 06632f195e
5 changed files with 33 additions and 29 deletions

View file

@ -38,5 +38,5 @@ select = [
# Line length is set to 88 for ruff. This is what the formatter aims for # Line length is set to 88 for ruff. This is what the formatter aims for
# some lines are not formatted to 88 if the formatter can't easily break # some lines are not formatted to 88 if the formatter can't easily break
# them. Allow up to 99 beofre throwing a long line error # them. Allow up to 99 before throwing a long line error
line-length = 99 line-length = 99

View file

@ -22,7 +22,7 @@ XYZPosList: TypeAlias = list[XYZPos]
def enforce_xy_tuple(value: XYPos) -> XYPos: def enforce_xy_tuple(value: XYPos) -> XYPos:
""" """
Used for enfocring that an input is a tuple and is the correct length Used for enforcing that an input is a tuple and is the correct length
""" """
if not isinstance(value, (list, tuple)): if not isinstance(value, (list, tuple)):
raise ValueError("2 value tuple expected") raise ValueError("2 value tuple expected")
@ -35,7 +35,7 @@ def enforce_xy_tuple(value: XYPos) -> XYPos:
def enforce_xyz_tuple(value: XYZPos) -> XYZPos: def enforce_xyz_tuple(value: XYZPos) -> XYZPos:
""" """
Used for enfocring that an input is a tuple and is the correct length Used for enforcing that an input is a tuple and is the correct length
""" """
if not isinstance(value, (list, tuple)): if not isinstance(value, (list, tuple)):
raise ValueError("3 value tuple expected") raise ValueError("3 value tuple expected")
@ -64,7 +64,7 @@ class ScanPlanner:
locations are adjusted. locations are adjusted.
When subclassing be sure to use enforce_xy_tuple and enforce_xyz_tuple on any user When subclassing be sure to use enforce_xy_tuple and enforce_xyz_tuple on any user
data before data before running
""" """
def __init__(self, intial_position: XYPos, planner_settings: Optional[dict] = None): def __init__(self, intial_position: XYPos, planner_settings: Optional[dict] = None):
@ -199,15 +199,15 @@ class ScanPlanner:
path_pos = np.array(self._focused_locations, dtype="float64")[:, :2] path_pos = np.array(self._focused_locations, dtype="float64")[:, :2]
# Use linalg.norm to calculate the direct distance bweween the points # Use linalg.norm to calculate the direct distance bweween the points
# Note linalg.norm always used float64 # Note linalg.norm always uses float64
dists = np.linalg.norm((path_pos - current_pos), axis=1) dists = np.linalg.norm((path_pos - current_pos), axis=1)
# Get indicies of all mimuma. # Get indices of all minima.
# Note np.where always returns a tuple of arrays, hence the trailing [0] # Note np.where always returns a tuple of arrays, hence the trailing [0]
indicies = np.where(dists == np.min(dists))[0] indices = np.where(dists == np.min(dists))[0]
# The last index is most recent # The last index is most recent
return self._focused_locations[indicies[-1]] return self._focused_locations[indices[-1]]
def mark_location_visited( def mark_location_visited(
self, xyz_pos: XYZPos, imaged: bool, focused: bool self, xyz_pos: XYZPos, imaged: bool, focused: bool
@ -216,7 +216,7 @@ class ScanPlanner:
Mark the location as visited Mark the location as visited
Args: Args:
xyz_pos: the x_y poistion xyz_pos: the x_y_z position
imaged: true if an image was taken, false if not (due to background detect) imaged: true if an image was taken, false if not (due to background detect)
focused: true if autofocus completed successfully focused: true if autofocus completed successfully
""" """
@ -289,10 +289,10 @@ class SmartSpiral(ScanPlanner):
self, xyz_pos: XYZPos, imaged: bool = True, focused: bool = True self, xyz_pos: XYZPos, imaged: bool = True, focused: bool = True
) -> None: ) -> None:
""" """
Mark the location as visited. Adjust extra poisitons accordingly Mark the location as visited. Adjust extra positions accordingly
Args: Args:
xyz_pos: the x_y poistion xyz_pos: the x_y_z position
imaged: true if an image was taken, false if not (due to background detect) imaged: true if an image was taken, false if not (due to background detect)
focused: true if autofocus completed successfully focused: true if autofocus completed successfully
""" """
@ -306,9 +306,9 @@ class SmartSpiral(ScanPlanner):
def _add_surrounding_positions(self, xy_pos: XYPos) -> None: def _add_surrounding_positions(self, xy_pos: XYPos) -> None:
""" """
This adds the surrounding (4 point connectivity) poistions This adds the surrounding (4 point connectivity) positions
to the remaining locations if they are not too far away or to the remaining locations if they are not too far away or
or already planned or already visited already planned or already visited
""" """
new_positions = [ new_positions = [
(xy_pos[0] - self._dx, xy_pos[1]), (xy_pos[0] - self._dx, xy_pos[1]),
@ -318,7 +318,7 @@ class SmartSpiral(ScanPlanner):
] ]
for new_pos in new_positions: for new_pos in new_positions:
# Skip position if already planned or fixited # Skip position if already planned or visited
if self.position_planned(new_pos) or self.position_visited(new_pos): if self.position_planned(new_pos) or self.position_visited(new_pos):
continue continue

View file

@ -67,7 +67,11 @@ def unpack_autofocus(scan_data):
def focus_change_acceptable(prev_pos, prev_z, new_pos, new_z, fractional_limit): def focus_change_acceptable(prev_pos, prev_z, new_pos, new_z, fractional_limit):
# limit is the largest ratio of change in z to change in xy that's allowed """Return true if the change in z-position is small enough.
A large change in z-position in an indication of focus failure.
fractional_limit is the largest ratio of change in z to change in xy that's allowed
"""
prev_xy = np.asarray(prev_pos, dtype="float64") prev_xy = np.asarray(prev_pos, dtype="float64")
new_xy = np.asarray(new_pos, dtype="float64") new_xy = np.asarray(new_pos, dtype="float64")
@ -100,7 +104,7 @@ def ensure_free_disk_space(path: str, min_space: int = 500000000) -> None:
default = 500,000,000 (500MiB) default = 500,000,000 (500MiB)
Raises: Raises:
NotEnoughFreeSpaceError is the NotEnoughFreeSpaceError if the remaining storage is below min_space
""" """
du = shutil.disk_usage(path) du = shutil.disk_usage(path)
if du.free < min_space: if du.free < min_space:
@ -449,7 +453,7 @@ class SmartScanThing(Thing):
) )
autofocus_dz = 0 autofocus_dz = 0
# Fix scan parameters in case UI is updates during scan. # Fix scan parameters in case UI is updated during scan.
self._scan_data = { self._scan_data = {
"scan_name": self._ongoing_scan_name, "scan_name": self._ongoing_scan_name,
"overlap": overlap, "overlap": overlap,
@ -464,7 +468,7 @@ class SmartScanThing(Thing):
} }
@_scan_running @_scan_running
def _save_scan_inputs_jons(self): def _save_scan_inputs_json(self):
# This should be a method of the scan_data dataclass # This should be a method of the scan_data dataclass
data = { data = {
@ -504,7 +508,7 @@ class SmartScanThing(Thing):
try: try:
self._set_scan_data() self._set_scan_data()
self._save_scan_inputs_jons() self._save_scan_inputs_json()
if self._scan_images_taken != 0: if self._scan_images_taken != 0:
msg = "_scan_images_taken should be zero before starting scanning" msg = "_scan_images_taken should be zero before starting scanning"
raise RuntimeError(msg) raise RuntimeError(msg)
@ -575,7 +579,7 @@ class SmartScanThing(Thing):
new_pos_xyz = self._move_to_next_point(next_pos_xy, z_est) new_pos_xyz = self._move_to_next_point(next_pos_xy, z_est)
capture_image = True capture_image = True
# If skipping background, take and image to check if is background # If skipping background, take an image to check if it is background
if self._scan_data["skip_background"]: if self._scan_data["skip_background"]:
capture_image = self._background_detect.image_is_sample() capture_image = self._background_detect.image_is_sample()
@ -583,7 +587,7 @@ class SmartScanThing(Thing):
route_planner.mark_location_visited( route_planner.mark_location_visited(
new_pos_xyz, imaged=False, focused=False new_pos_xyz, imaged=False, focused=False
) )
# Background franction is actually a percentage # Background fraction is actually a percentage
back_perc = round(self._background_detect.background_fraction(), 0) back_perc = round(self._background_detect.background_fraction(), 0)
msg = f"Skipping {new_pos_xyz} as it is {back_perc}% background." msg = f"Skipping {new_pos_xyz} as it is {back_perc}% background."
self._scan_logger.info(msg) self._scan_logger.info(msg)
@ -826,8 +830,8 @@ class SmartScanThing(Thing):
).encode("utf-8") ).encode("utf-8")
piexif.insert(piexif.dump(exif_dict), jpeg_path) piexif.insert(piexif.dump(exif_dict), jpeg_path)
except: # noqa: E722 except: # noqa: E722
# We need to capture any exeption as there are many reasons metadata # We need to capture any exception as there are many reasons metadata
# might not be added. We wern rather than log the error. # might not be added. We warn rather than log the error.
self._scan_logger.warning(f"Failed to add metadata to {jpeg_path}") self._scan_logger.warning(f"Failed to add metadata to {jpeg_path}")
except Exception as e: except Exception as e:
raise IOError(f"An error occurred while saving {jpeg_path}") from e raise IOError(f"An error occurred while saving {jpeg_path}") from e

View file

@ -39,7 +39,7 @@ def test_v_basic_smart_spiral():
planner = scan_planners.SmartSpiral( planner = scan_planners.SmartSpiral(
intial_position=intial_position, planner_settings=planner_settings intial_position=intial_position, planner_settings=planner_settings
) )
# Create a planner it shouldn't start complete # Create a planner. It shouldn't be complete.
assert not planner.scan_complete assert not planner.scan_complete
# When we start it should want to stay in the inital pos and have # When we start it should want to stay in the inital pos and have
# no z_estimate # no z_estimate
@ -47,7 +47,7 @@ def test_v_basic_smart_spiral():
assert xy_pos == intial_position assert xy_pos == intial_position
assert z_pos is None assert z_pos is None
# Try to make imaged with only xy_position # Try to mark location as imaged with only xy_position
with pytest.raises(ValueError): with pytest.raises(ValueError):
planner.mark_location_visited(xy_pos, imaged=False, focused=False) planner.mark_location_visited(xy_pos, imaged=False, focused=False)
# scan still not complete # scan still not complete
@ -100,7 +100,7 @@ def test_smart_spiral_first_few_pos():
This test is VERY long, not really a "unit". It checks step-by-step This test is VERY long, not really a "unit". It checks step-by-step
that data is added correctly for the first few postions in a scan. that data is added correctly for the first few postions in a scan.
This should catch basic caseses of if the algorithm is updated. This should catch basic cases of if the algorithm is updated.
""" """
intial_position = (100, 50) intial_position = (100, 50)
planner_settings = {"dx": 50, "dy": 50, "max_dist": 10000} planner_settings = {"dx": 50, "dy": 50, "max_dist": 10000}

View file

@ -31,7 +31,7 @@ class FakeSample:
Return whether an image at a given location with a given image size Return whether an image at a given location with a given image size
is on the sample is on the sample
This doesn't check the entire image feild as this is designed to be used This doesn't check the entire image field as this is designed to be used
where the fake sample is much larger than the image and has smooth edges where the fake sample is much larger than the image and has smooth edges
It just checks the 4 corners It just checks the 4 corners
""" """
@ -48,7 +48,7 @@ class FakeSample:
@property @property
def patch(self) -> PathPatch: def patch(self) -> PathPatch:
""" """
The sample as a matplotlib patch fro plotting The sample as a matplotlib patch for plotting
""" """
patch = PathPatch(self._sample_perimeter) patch = PathPatch(self._sample_perimeter)
patch.set(color=(1.0, 0.8, 1.0, 1.0)) patch.set(color=(1.0, 0.8, 1.0, 1.0))
@ -86,7 +86,7 @@ def visualise_scan(sample: FakeSample, planner: scan_planners.ScanPlanner) -> Fi
def interp_closed_path(xy_points: list[tuple[int, int]], n_points: int) -> MatPath: def interp_closed_path(xy_points: list[tuple[int, int]], n_points: int) -> MatPath:
""" """
Given a lists of xy_points interpolate an n_point closed curve. This can be used Given a lists of xy_points interpolate an n_point closed curve. This can be used
to creat an arbitrary sample shape plan a scan. to create an arbitrary sample shape plan a scan.
Modified from: Modified from:
https://stackoverflow.com/questions/33962717/interpolating-a-closed-curve-using-scipy https://stackoverflow.com/questions/33962717/interpolating-a-closed-curve-using-scipy