Source code for tike.scan

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# #########################################################################
# Copyright (c) 2018, UChicago Argonne, LLC. All rights reserved.    #
#                                                                         #
# Copyright 2018. UChicago Argonne, LLC. This software was produced       #
# under U.S. Government contract DE-AC02-06CH11357 for Argonne National   #
# Laboratory (ANL), which is operated by UChicago Argonne, LLC for the    #
# U.S. Department of Energy. The U.S. Government has rights to use,       #
# reproduce, and distribute this software.  NEITHER THE GOVERNMENT NOR    #
# UChicago Argonne, LLC MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR        #
# ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE.  If software is     #
# modified to produce derivative works, such modified software should     #
# be clearly marked, so as not to confuse it with the version available   #
# from ANL.                                                               #
#                                                                         #
# Additionally, redistribution and use in source and binary forms, with   #
# or without modification, are permitted provided that the following      #
# conditions are met:                                                     #
#                                                                         #
#     * Redistributions of source code must retain the above copyright    #
#       notice, this list of conditions and the following disclaimer.     #
#                                                                         #
#     * Redistributions in binary form must reproduce the above copyright #
#       notice, this list of conditions and the following disclaimer in   #
#       the documentation and/or other materials provided with the        #
#       distribution.                                                     #
#                                                                         #
#     * Neither the name of UChicago Argonne, LLC, Argonne National       #
#       Laboratory, ANL, the U.S. Government, nor the names of its        #
#       contributors may be used to endorse or promote products derived   #
#       from this software without specific prior written permission.     #
#                                                                         #
# THIS SOFTWARE IS PROVIDED BY UChicago Argonne, LLC AND CONTRIBUTORS     #
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT       #
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS       #
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UChicago     #
# Argonne, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,        #
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,    #
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;        #
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER        #
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT      #
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN       #
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         #
# POSSIBILITY OF SUCH DAMAGE.                                             #
# #########################################################################
"""Define building blocks of scanning trajectories and related functions.

Each trajectory returns position as a function of time and some other
parameters.

.. |t_docstring| replace:: Time steps to evaluate the function.
"""

__author__ = "Doga Gursoy, Daniel Ching"
__copyright__ = "Copyright (c) 2018, UChicago Argonne, LLC."
__docformat__ = 'restructuredtext en'
__all__ = [
    'scantimes',
    'sinusoid',
    'triangle',
    'triangle_fs',
    'sawtooth',
    'square',
    'staircase',
    'lissajous',
    'raster',
    'spiral',
    'diagonal',
    'scan3',
    'avgspeed',
    'lengths',
    'distance',
    'billiard',
    'hexagonal',
]

import logging
import numpy as np

logger = logging.getLogger(__name__)


def _periodic_function_interface(t, A=0.5, f=60, p=0):
    """Define an exemplar periodic function for this module.

    Each trajectory function is a function of t, an array of time
    steps, and optional keyword arguements which determine the shape of
    the function. The function returns at least one spatial coordinate.

    Parameters
    ----------
    t : np.array
        |t_docstring|
    A : float
        The amplitude of the function.
    f : float
        The temporal frequency of the function.
    p : float [radians]
        The phase shift of the function.

    """
    raise NotImplementedError()


[docs]def hexagonal(t, D, f, row): """Hexagonal gridded step scan of circles with diameter, D. #discontinuous #2d Parameters ---------- t : np.array |t_docstring| D : float The diameter of the circles. f : float How often to we move to the next circle. row : int The number of positions in each row """ h = 0.5 * np.sqrt(3) * D x1 = staircase(A=h, f=f / row, p=0, t=t) x2 = (np.mod(staircase(A=D, f=f, p=0, t=t), row * D) + square( A=D * 0.25, f=f / row * 0.5, p=np.pi, t=t) + D * 0.25) return x1, x2
def f2w(f): """Return the angular frequency [rad] from the given frequency.""" return 2 * np.pi * f def period(f): """Return the period from the given frequency.""" return 1 / f
[docs]def scantimes(t0, t1, f=60): """Return times in the range [t0, t1) at the given frequency (f).""" return np.linspace(t0, t1, (t1 - t0) * f, endpoint=False)
[docs]def sinusoid(A, f, p, t): """Return the value of a sine function at time `t`. #continuous #1D Parameters ---------- t : np.array |t_docstring| A : float The amplitude of the function. f : float The temporal frequency of the function. p : float [radians] The phase shift of the function. """ w = f2w(f) return A * np.sin(w * t - p)
[docs]def triangle(A, f, p, t): """Return the value of a triangle function at time `t`. #continuous #1d Parameters ---------- t : np.array |t_docstring| A : float The amplitude of the function. f : float The temporal frequency of the function. p : float [radians] The phase shift of the function. """ w = f2w(f) return A * 2 / np.pi * np.arcsin(np.sin(w * t - p))
[docs]def triangle_fs(A, f, p, t, N=8): """Approximate the triangle function using a Fourier series of N sinusoids. #continuous #1d """ w = f2w(f) x = np.sin(w * t - p) for n in range(3, 2 * N, 2): x += (-1)**((n - 1) / 2) / (n * n) * np.sin(n * (w * t - p)) return A * 8 / np.pi / np.pi * x
[docs]def sawtooth(A, f, p, t): """Return the value of a sawtooth function at time `t`. #discontinuous #1d Parameters ---------- t : np.array |t_docstring| A : float The amplitude of the function. f : float The temporal frequency of the function. p : float [radians] The phase shift of the function. """ ts = t * f - p / (2 * np.pi) q = np.floor(ts + 0.5) return A * (2 * (ts - q))
[docs]def square(A, f, p, t): """Return the value of a square function at time `t`. #discontinuous #1d Parameters ---------- t : np.array |t_docstring| A : float The amplitude of the function. f : float The temporal frequency of the function. p : float [radians] The phase shift of the function. """ ts = t - p / (2 * np.pi) / f return A * (np.power(-1, np.floor(2 * f * ts)))
[docs]def staircase(A, f, p, t): """Return the value of a staircase function at time `t`. #discontinuous #1d Parameters ---------- t : np.array |t_docstring| A : float The amplitude of the function. f : float The temporal frequency of the function. p : float [radians] The phase shift of the function. """ ts = t * f - p / (2 * np.pi) return A * np.floor(ts)
[docs]def lissajous(A, B, fx, fy, px, py, t): """Return the value of a lissajous function at time `t`. #continuous #2d The lissajous is centered on the origin. The lissajous is periodic if and only if the fx / fy is rational. The overall period is the least common multiple of the two periods. Parameters ---------- t : np.array |t_docstring| A, B : float The horizontal and vertical amplitudes of the function fx, fy : float The temporal frequencies of the function px, py : float The phase shifts of the x and y components of the function """ x = sinusoid(A, fx, px, t) y = sinusoid(B, fy, py, t) return x, y
[docs]def billiard(Ax, Ay, fx, fy, px, py, t, N): """Return the trajectory of a frictionless ball in a rectangular table. #continuous #2d It's a lissajous using triangle functions. """ x = triangle_fs(Ax, fx, px, t, N) y = triangle_fs(Ay, fy, py, t, N) return x, y
[docs]def raster(A, B, f, x0, y0, t): """Return the value of a raster function at time `t`. #discontinuous #2d The raster starts at (x0, y0) and moves initially in the positive directions. Parameters ---------- t : np.array |t_docstring| A : float The horizontal length of lines B : float The vertical space between lines f : float The number of raster lines per second x0, y0 : float Starting positions of the raster """ x = 0.5 * (triangle(A, 0.5 * f, 0.5 * np.pi, t) + A) + x0 y = staircase(B, f, 0, t) + y0 return x, y
[docs]def spiral(r1, t1, v, t): """Return a spiral of constant linear velcity at time `t`. #continuous #2d The spiral is centered on the origin and spins clockwise. Parameters ---------- t : np.array |t_docstring| r1 : float The radius at time t1. t1: float The time at which the spiral reaches r1. v : float The linear velocity of the trajectory References ---------- A. Bazaei, M. Maroufi and S. O. R. Moheimani, "Tracking of constant-linear-velocity spiral trajectories by approximate internal model control," 2017 IEEE Conference on Control Technology and Applications (CCTA), Mauna Lani, HI, 2017, pp. 129-134. doi: 10.1109/CCTA.2017.8062452 """ P = np.pi * r1 * r1 / t1 / v r = np.sqrt(P * v * t / np.pi) theta = 2 * np.sqrt(np.pi * v * t / P) x = r * np.cos(theta) y = r * np.sin(theta) return x, y
[docs]def diagonal(A, B, fx, fy, px, py, t): """Return the value of a diagonal function at time `t`. #discontinuous #2d The diagonal is centered on the origin. Parameters ---------- A, B : float The horizontal and vertical amplitudes of the function fx, fy : float The temporal frequencies of the function px, py : float The phase shifts of the x and y components of the function t : np.array |t_docstring| """ x = triangle(A, fx, px + np.pi / 2, t) y = triangle(B, fy, py + np.pi / 2, t) return x, y
[docs]def scan3(A, B, fx, fy, fz, px, py, time, hz): """Return a 3D combination of lissajous and sawtooth trajectories.""" x, y, t = lissajous(A, B, fx, fy, px, py, time, hz) z = sawtooth(np.pi, 0.5 * fz, 0.5 * np.pi, t, hz) return z, x, y, t
[docs]def avgspeed(time, x, y=None, z=None): """Return the average speed along trajectory x, y, z if covered in time.""" return distance(z, x, y) / time
[docs]def lengths(x, y=None, z=None): """Return the absolute displacements between points defined by x, y, z.""" if y is None: y = np.zeros(x.shape) if z is None: z = np.zeros(x.shape) a = np.diff(x) b = np.diff(y) c = np.diff(z) return np.sqrt(a * a + b * b + c * c)
[docs]def distance(x, y=None, z=None): """Return the total distance travelled along the trajectory x, y, z.""" d = lengths(z, x, y) return np.sum(d)