======================= Calibrating Delay Scans ======================= Performing uncalibrated scans using the ``delay`` macromotor results in positional instability of the beam at the sample. To address this, ``delay`` has several methods to characterize and correct the scan. .. warning:: The functionality described here has not been commissioned, and should be used with caution. Characterizing Beam Instability =============================== The main way to characterize the current scan instability is to run the ``centroid_scan`` method. The method performs a scan using the inputted scan parameters, measuring the centroids of the beam at Opal 1. The results are then saved and graphed for the user to see. :: In [1]: snd.delay.centroid_scan(start, stop, step) To view the centroids again after the scan has been completed, the method ``view_centroids`` can be used to view the centroids from the most recent ``centroids_scan``. :: In [2]: snd.delay.view_centroids() To view the graph displayed after the scan, run the ``graph_centroids`` method. :: In [3]: snd.delay.graph_centroids() .. note:: The results shown by ``view_centroids`` and ``graph_centroids`` come from the recent centroid scan or calibration. Anytime a new centroid scan is run, the previous scan data is overwritten. Calibration Operations ====================== In the event ``centroid_scan`` reveals the beam drifts too much throughout the scan, there is a routine for building a correction table using the ``snd.t1.chi1`` and ``snd.t1.y1`` motors to correct for the x and y centroids respectively. This correction table is put together using the table generated from a ``centroid_scan`` along with parameters to find the scaling information between each of the motor positions and the beam centroids. Together all the information collected during a ``calibrate`` run, constitutes a specific calibration. High-Level Properties --------------------- The ``delay`` macromotor comes with two high level properties pertaining to the current state of the calibration. ``has_calibration`` indicates that a properly configured calibration is currently loaded to the motor, simply returning a ``bool``. The second high level property is ``use_calibration`` which determines whether a calibration should be used during a move. By default, anytime a calibration is created or loaded, ``use_calibration`` is set to be ``True``. However, this is primarily useful if there is a move where performing a the correction is not desired. By setting ``use_calibration`` to ``False`` all moves will be done without corrections .. note:: Motor corrections will only be performed on every ``move`` command if both ``has_calibration`` and ``use_calibration`` are ``True``. Creating a New Calibration -------------------------- If a new calibration is necessary, the entire calibration routine can be run using the ``calibrate`` method along with the desired scan parameters. :: In [1]: snd.delay.calibrate(start, stop, step) The calibration routine itself consists of first performing a centroid scan to measure the current beam drift, and then performing a walk using each of the calibration motors to find the motor position to centroid scaling. Once, complete, the motor will automatically start using the new calibration for all subsequent ``move`` commands. Additionally, all the motors will be returned to their original positions after the routine has completed. .. note:: A confirmation to overwrite the existing calibration will be required to run ``calibrate`` if the motor already has a valid configuration. Saving and Loading Calibrations ------------------------------- Calibrations used for the ``delay`` motor are all saved in the ``HXRSnD/calibrations`` directory. Whenever a ``calibration`` routine has completed, the calibration is saved into a file named ``current_calibration.json``. To save a calibration more permanently, the method ``save_calibration`` can be used by passing a either filename or a full path. Running, :: In [1]: snd.delay.save_calibration('my_calibration') Will save the current calibration as ``HXRSnD/calibrations/my_calibration.json``. To load this configuration, the ``load_calibration`` method takes calibration names, searches the ``HXRSnD/calibrations`` directory, and then applies the dound calibration. For example, :: In [2]: snd.delay.load_calibration('my_calibration') loads the ``my_calibration.json`` from ``HXRSnD/calibrations``. .. note:: Running ``load_calibration`` without any inputs automatically loads ``current_calibration.json``. Inspecting the Calibration ========================== To view the contents of a calibration, the ``calibration`` property returns a dictionary with five keys: - calib : ``DataFrame`` containing the correction table used by the calibration motors. - motors : List of ``Motor`` objects that refer to the motors used by the calibration. - scan : ``DataFrame`` containing the results of the centroid scan used to build the correction table. - scale : List if values in units of ps/pixel, used to convert between pixel positions and delay position. - start : List of starting positions of the motors used to calculate the scale. .. note:: ``calibration`` is a read-only property and cannot be used to modify the live calibration. Modifying Calibrations ====================== Each of the five calibration values listed above can be modified outright using the ``configure`` method. This is done by running ``configure`` and passing the desired change as a keyword argument. .. warning:: It is not advisable to change the the calibration in ways not listed below. Correction Table Post-Processing -------------------------------- A simple reason to modify the correction table would be to apply some level of post processing to the resulting table. For example, to apply a Savitzky-Golay smoothing filter, first capturet he current correction table in a new dataframe using the ``calibration`` property, :: In [1]: df_calib = snd.delay.calibration['calib'] Then create a new dataframe with the applied filter using some ``window_length`` and ``polyorder`` (see documentation on ``scipy.signal.savgol_filter`` for more details), :: In [2]: df_savgol = df_calib.apply(savgol_filter, args=(window_length, polyorder)) And then configure the motor to use this new correction table, :: In [3]: snd.delay.configure(calib=df_savgol) .. note:: Whenever the correction table is modified using ``configure``, the number of columns must equal the number of motors listed in ``snd.delay.calibration['motors']``. Rescaling the Correction Table ------------------------------ In the event that the picosecond per pixel scaling factor may need to be redone, the method ``rescale_calibration`` will perform the scaling routine and then update the calibration accordingly. :: In [1]: snd.delay.rescale_calibration()