<jupyter_start><jupyter_text>Simulated 2-D PointingThis notebook contains a simulation for two motors that control the position of a point on an imager. Each motor controls one axis of the position of centroid along the screen<jupyter_code># General setup and imports
%matplotlib notebook
import matplotlib.pyplot as plt
from bluesky import RunEngine
from bluesky.callbacks.best_effort import BestEffortCallback
from bluesky.plans import *
from bluesky.preprocessors import run_wrapper
from bluesky.utils import install_nb_kicker
from functools import partial
from ophyd import Device, Component as Cpt
from ophyd.sim import SynAxis, SynSignal
import pcdsdevices

# Interactive matplotlib mode
plt.ion()<jupyter_output><empty_output><jupyter_text>Device DefinitionsBefore we can start scanning we need to instantiate a few devices for our simulation:* `RunEngine` - Think of this as the conductor for our orchestra. The job of the RunEngine is to direct the rest of the band as we perform our scans* `SynAxis` - These are built-in simulation motors from `ophyd`. They are a simple little class which just goes where it is told.* `SynSignal` - This class will take the place of our detector. It allows us to plug in a Python function that is evaluated every time we "trigger" it. In this case the function we create will interpret the position of our motors and return where the centroid values are present.<jupyter_code># Create a RunEngine
RE = RunEngine()
# Use BestEffortCallback for nice vizualizations during scans
bec = BestEffortCallback()
RE.subscribe(bec)
# Install our notebook kicker to have plots update during a scan
install_nb_kicker()
# Create our motors
x = SynAxis(name='x')
y = SynAxis(name='y')
# Define our function to determine motor position
def centroid_from_motor(motor, size=640., scale=1.):
    # Find the current position of the motor
    position = motor.position
    # Calculate the centroid position
    centroid = position * scale
    # If we are off the screen just return a value of 0.
    if centroid < 0. or centroid > size:
        return 0.
    # Otherwise give the result of our line
    else:
        return centroid
        
    
class Imager(Device):
    # Declare the relationships between motors and centroids
    x_centroid = Cpt(SynSignal, func=partial(centroid_from_motor, x))
    y_centroid = Cpt(SynSignal, func=partial(centroid_from_motor, y))
    
    # Setting the hints is not strictly required but this allows the 
    # automated vizualization to pick up on these values
    @property
    def hints(self):
        return {'fields': [self.x_centroid.name, self.y_centroid.name]}
    
    # Make sure we update all the signals when the detector is triggered
    def trigger(self):
        self.x_centroid.trigger()
        self.y_centroid.trigger()
        return super().trigger()
imager = Imager(name='imager')<jupyter_output><empty_output><jupyter_text>PlaygroundNow that we have our devices are setup, feel free to play around and run some scans. I've included a few examples below to get you started.<jupyter_code>RE(scan([imager], x, -100, 800, 50))
RE(grid_scan([imager], x, 200, 400, 10, y, 200, 400, 10, False))<jupyter_output><empty_output><jupyter_text>AlignmentWhile the prior examples are all built-in to `bluesky`, there has been development at **SLAC** on scans focusing more on automation than pure data collection. These use the "adaptive" behavior possible with the `bluesky` framework to collect data, interpet it as it streams in, then make the next step with more conviction.<jupyter_code>from pswalker.plans import walk_to_pixel
RE(run_wrapper(walk_to_pixel(imager, x, 420, start=40, first_step=20, target_fields=['x_centroid', 'x'], max_steps=5)))<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.06902985074626866, 'fraction_numerical': 0.010660980810234541, 'mean_word_length': 4.626686656671664, 'pattern_counts': {'":': 0, '<': 18, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '586712', 'license': 'BSD-3-Clause-LBNL', 'n_tokens_mistral': 1122, 'n_tokens_neox': 1055, 'n_words': 453, 'path': 'notebooks/2d_simulation.ipynb', 'repo_name': 'pcdshub/time_zero', 'size': '3752'}