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
2 changes: 1 addition & 1 deletion src/instamatic/calibrate/calibrate_stage_motion.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ def plot(self, sst: Optional[Sequence[SpanSpeedTime]] = None) -> None:
spans = [s.span for s in sst if s.speed == speed]
times = [s.time for s in sst if s.speed == speed]
ax.plot(spans, times, fmt, color=color)
label = f'Speed setting {speed:.2f}'
label = f'Speed setting {speed:.2f}' if speed else 'Default speed'
handles.append(Line2D([], [], color=color, marker='o', label=label))

ax.set_xlabel(f'Motion span [{self._span_units}]')
Expand Down
29 changes: 22 additions & 7 deletions src/instamatic/calibrate/calibrate_stage_rotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import argparse
import logging
from contextlib import nullcontext
from dataclasses import dataclass
from textwrap import dedent
from time import perf_counter
Expand Down Expand Up @@ -71,7 +72,8 @@ def calibrate_stage_rotation_live(
spans: `Optional[Sequence[float]]`
Alpha rotations whose speed will be measured. Default: range(1, 11, 1).
speeds: `Optional[Sequence[Union[float, int]]]`
Spead range to measure. Default: range(1, 13, 1) or range(.1, 1.1, .1).
Spead range to measure. Default: range(1, 13, 1) or range(.1, 1.1, .1),
or [None, None, None], 3 runs without setting speed if it's not possible
mode: `Literal['auto', 'limited', 'listed']`
Determines the way speed settings restrictions are set in calib file.
outdir: `str` or None
Expand All @@ -91,6 +93,10 @@ def calibrate_stage_rotation_live(
except AssertionError: # or any other raised if speed can't be set
speeds_default = np.linspace(0.01, 0.2, num=20)
speed_options = FEI_ROTATION_SPEED_OPTIONS
except (AttributeError, ConnectionError, TypeError):
log('TEM does not support setting with speed, assuming default = 1.')
speeds_default: Sequence[Speed] = [None, None, None] # no translation w/ speed
speed_options = None
else:
speeds_default = np.arange(1, 13, step=1)
speed_options = JEOL_ROTATION_SPEED_OPTIONS
Expand All @@ -101,16 +107,20 @@ def calibrate_stage_rotation_live(
elif mode == 'listed':
speed_options = NumericDomain(options=sorted(speeds_))

try:
starting_stage_speed = ctrl.stage.get_rotation_speed()
except (AttributeError, ConnectionError, TypeError):
starting_stage_speed = None

calib_points: list[SpanSpeedTime] = []
starting_stage_alpha = ctrl.stage.a
starting_stage_speed = ctrl.stage.get_rotation_speed()
ctrl.cam.block()
try:
n_calib_points = len(speeds_) * len(spans_array)
log(f'Calibrating a-axis rotation speed based on {n_calib_points} points.')
with tqdm(total=n_calib_points) as progress_bar:
for speed in speeds_:
with ctrl.stage.rotation_speed(speed=float(speed)):
with ctrl.stage.rotation_speed(float(speed)) if speed else nullcontext():
ctrl.stage.a = 0.0
for target, span in zip(alpha_targets, spans_array):
t1 = perf_counter()
Expand All @@ -120,7 +130,8 @@ def calibrate_stage_rotation_live(
progress_bar.update(1)
finally:
ctrl.stage.set(a=starting_stage_alpha)
ctrl.stage.set_rotation_speed(starting_stage_speed)
if starting_stage_speed is not None:
ctrl.stage.set_rotation_speed(starting_stage_speed)
ctrl.cam.unblock()

c = CalibStageRotation.from_data(calib_points)
Expand Down Expand Up @@ -149,7 +160,8 @@ def main_entry() -> None:

h = """Comma-delimited list of speed settings to calibrate for.
Default: "0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0" or
"1 2 3 4 5 6 7 8 9 10 11 12", whichever is accepted by the microscope."""
"1 2 3 4 5 6 7 8 9 10 11 12", whichever is accepted by the microscope. "-s"
with no values forces 3 rounds using "set" instead of "set_with_speed"."""
h = dedent(h.strip())
parser.add_argument('-s', '--speeds', type=float, default=None, nargs='*', help=h)

Expand All @@ -169,8 +181,11 @@ def main_entry() -> None:
parser.add_argument('--plot', action=argparse.BooleanOptionalAction, default=True, help=h)

kwargs = vars(parser.parse_args())
if kwargs['speeds'] is not None and all(v.is_integer() for v in kwargs['speeds']):
kwargs['speeds'] = [int(v) for v in kwargs['speeds']]
if kwargs['speeds'] is not None:
if all(v.is_integer() for v in kwargs['speeds']):
kwargs['speeds'] = [int(v) for v in kwargs['speeds']]
if not kwargs['speeds']:
kwargs['speeds'] = [None, None, None]

from instamatic import controller

Expand Down
17 changes: 11 additions & 6 deletions src/instamatic/calibrate/calibrate_stage_translation.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ def calibrate_stage_translation_live(
spans: `Optional[Sequence[float]]`
Translations that will be timed. Default: 10_000 to 100_000 every 10_000
speeds: `Optional[Sequence[Union[float, int]]]`
All speed settings used for calibration. Default: 0.1 to 1.0 every 0.1.
All speed settings used for calibration. Default: 0.1 to 1.0 every 0.1,
or [None, None, None], 3 runs without setting speed if it's not possible
axis: `Literal['x', 'y', 'z']`
Axis the speed of which is to be calibrated. Default: 'x'.
mode: `Literal['auto', 'limited', 'listed']`
Expand All @@ -118,7 +119,7 @@ def calibrate_stage_translation_live(
stage0: StagePositionTuple = ctrl.stage.get()
try:
ctrl.stage.set_with_speed(*stage0)
except KeyError:
except TypeError:
log('TEM does not support setting with speed, assuming default = 1.')
speeds_default: Sequence[Speed] = [None, None, None] # no translation w/ speed
speed_options = None
Expand Down Expand Up @@ -180,8 +181,9 @@ def main_entry() -> None:
h += 'Default: "10000 20000 30000 40000 50000 60000 70000 80000 90000 100000".'
parser.add_argument('-x', '--spans', type=int, default=None, nargs='*', help=h)

h = 'Comma-delimited list of speed settings to calibrate for.'
h += 'Default: "0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0".'
h = 'Comma-delimited list of speed settings to calibrate for. '
h += 'Default: "0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0". '
h += '"-s" with no values forces 3 rounds using "set" instead of "set_with_speed".'
parser.add_argument('-s', '--speeds', type=float, default=None, nargs='*', help=h)

h = 'Axis whose translation speed should be calibrated, x, y, or z. Default: x'
Expand All @@ -204,8 +206,11 @@ def main_entry() -> None:
parser.add_argument('--plot', action=argparse.BooleanOptionalAction, default=True, help=h)

kwargs = vars(parser.parse_args())
if kwargs['speeds'] is not None and all(v.is_integer() for v in kwargs['speeds']):
kwargs['speeds'] = [int(v) for v in kwargs['speeds']]
if kwargs['speeds'] is not None:
if all(v.is_integer() for v in kwargs['speeds']):
kwargs['speeds'] = [int(v) for v in kwargs['speeds']]
if not kwargs['speeds']:
kwargs['speeds'] = [None, None, None]

from instamatic import controller

Expand Down
6 changes: 3 additions & 3 deletions src/instamatic/camera/camera_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def _eval_dct(self, dct):
with self._eval_lock:
self.s.send(dumper(dct))

acquiring_image = dct['attr_name'] in {'get_image', 'get_movie', '__gen_next__'}
acquiring_image = dct['attr_name'] in {'get_image', '__gen_next__'}

if acquiring_image and not self.use_shared_memory:
response = self.s.recv(self._imagebufsize)
Expand All @@ -139,7 +139,7 @@ def _eval_dct(self, dct):
else:
raise RuntimeError(f'Received empty response when evaluating {dct=}')

if self.use_shared_memory and acquiring_image:
if self.use_shared_memory and acquiring_image and data:
data = self.get_data_from_shared_memory(**data)

if status == 200:
Expand Down Expand Up @@ -196,7 +196,7 @@ def get_data_from_shared_memory(self, name: str, shape: tuple, dtype: str, **kwa
print(f'Retrieve data from buffer `{name}`')

buffer = self.buffers[name]
data = buffer[:]
data = buffer[:].copy()

return data

Expand Down
39 changes: 30 additions & 9 deletions src/instamatic/microscope/interface/simu_microscope.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

import random
import time
from typing import Optional, Tuple
from contextlib import contextmanager, nullcontext
from typing import Literal, Optional, Tuple

from instamatic import config
from instamatic._typing import float_deg, int_nm
Expand Down Expand Up @@ -126,7 +127,7 @@ def __init__(self, name: str = 'simulate'):
speed = 20.0 # degree / sec
current = random.randint(-40, 40)
elif key in ('x', 'y'):
speed = 1_000_000.0 # nm / sec
speed = 100_000.0 # nm / sec
current = random.randint(-100000, 100000)
elif key == 'z':
speed = 100_000.0 # nm / sec
Expand Down Expand Up @@ -199,6 +200,19 @@ def _StagePositionGetter(self, var: str) -> float:

return ret

@contextmanager
def _speed_setting(self, key: Literal['a', 'b', 'x', 'y', 'z'], value: int):
previous_setting = self._stage_dict[key]['speed_setting']
previous_speed = self._stage_dict[key]['speed']
speed_base = {'x': 100_000, 'y': 100_000, 'z': 100_000, 'a': 20, 'b': 20}
try:
self._stage_dict[key]['speed_setting'] = value
self._stage_dict[key]['speed'] = speed_base[key] * value / 12
yield
finally:
self._stage_dict[key]['speed_setting'] = previous_setting
self._stage_dict[key]['speed'] = previous_speed

@property
def StagePosition_a(self):
return self._StagePositionGetter('a')
Expand Down Expand Up @@ -438,26 +452,33 @@ def setStagePosition(
wait: bool = True,
) -> None:
if z is not None:
self.setStageZ(z, wait=wait)
with nullcontext() if speed is None else self._speed_setting('z', speed):
self.setStageZ(z, wait=wait)
if a is not None:
self.setStageA(a, wait=wait)
with nullcontext() if speed is None else self._speed_setting('a', speed):
self.setStageA(a, wait=wait)
if b is not None:
self.setStageB(b, wait=wait)
with nullcontext() if speed is None else self._speed_setting('b', speed):
self.setStageB(b, wait=wait)

if (x is not None) and (y is not None):
self.setStageXY(x=x, y=y, wait=wait)
with nullcontext() if speed is None else self._speed_setting('x', speed):
with nullcontext() if speed is None else self._speed_setting('y', speed):
self.setStageXY(x=x, y=y, wait=wait)
else:
if x is not None:
self.setStageX(x, wait=wait)
with nullcontext() if speed is None else self._speed_setting('x', speed):
self.setStageX(x, wait=wait)
if y is not None:
self.setStageY(y, wait=wait)
with nullcontext() if speed is None else self._speed_setting('y', speed):
self.setStageY(y, wait=wait)

def getRotationSpeed(self) -> int:
return self._stage_dict['a']['speed_setting']

def setRotationSpeed(self, value: int):
self._stage_dict['a']['speed_setting'] = value
self._stage_dict['a']['speed'] = 10.0 * (value / 12)
self._stage_dict['a']['speed'] = 20.0 * (value / 12)

def getFunctionMode(self) -> str:
"""Mag1, mag2, lowmag, samag, diff."""
Expand Down
Loading