Source code for labscript.functions

#####################################################################
#                                                                   #
# /functions.py                                                     #
#                                                                   #
# Copyright 2013, Monash University                                 #
#                                                                   #
# This file is part of the program labscript, in the labscript      #
# suite (see http://labscriptsuite.org), and is licensed under the  #
# Simplified BSD License. See the license.txt file in the root of   #
# the project for the full license.                                 #
#                                                                   #
#####################################################################

"""Contains the functional forms of analog output ramps - these are not used directly,
instead see the interfaces in `AnalogQuantity`/`AnalogOut`."""

from pylab import *
import numpy as np

from .utils import print_time


[docs] def ramp(duration, initial, final): """Defines a linear ramp. f(t) = (final - initial)*t/duration + initial Args: duration (float): Duration of ramp initial (float): Starting value of ramp final (float): Ending value of ramp Returns: func: Function that takes a single parameter `t`. """ m = (final - initial)/duration return lambda t: m*t + initial
[docs] def sine(duration, amplitude, angfreq, phase, dc_offset): """Defines a sine wave. f(t) = amplitude*sin(angfreq*t + phase) + dc_offset Args: duration (float): Not used. amplitude (float): Amplitude of sine wave. angfreq (float): Angular frequency of sine wave. phase (float): Phase of sine wave. dc_offset (float): Verticle offset of sine wave. Returns: func: Function that takes a single parameter `t`. """ return lambda t: amplitude*sin(angfreq*(t) + phase) + dc_offset
[docs] def sine_ramp(duration, initial, final): """Defines a square sinusoidally increasing ramp. f(t) = (final-initial)*(sin(pi*t/(2*duration)))^2 + initial Args: duration (float): Length of time for the ramp to complete. initial (float): Initial value of ramp. final (float): Final value of ramp. Returns: func: Function that takes a single parameter `t`. """ return lambda t: (final-initial)*(sin(pi*(t)/(2*duration)))**2 + initial
[docs] def sine4_ramp(duration, initial, final): """Defines a quartic sinusoidally increasing ramp. f(t) = (final-initial)*(sin(pi*t/(2*duration)))^4 + initial Args: duration (float): Length of time for the ramp to complete. initial (float): Initial value of ramp. final (float): Final value of ramp. Returns: func: Function that takes a single parameter `t`. """ return lambda t: (final-initial)*(sin(pi*(t)/(2*duration)))**4 + initial
[docs] def sine4_reverse_ramp(duration, initial, final): """Defines a quartic sinusoidally decreasing ramp. f(t) = (final-initial)*(sin(pi/2+pi*t/(2*duration)))^4 + initial Args: duration (float): Length of time for the ramp to complete. initial (float): Initial value of ramp. final (float): Final value of ramp. Returns: func: Function that takes a single parameter `t`. """ return lambda t: (final-initial)*(sin(pi/2+pi*(t)/(2*duration)))**4 + initial
[docs] def exp_ramp(duration,initial,final,zero): """Defines an exponential ramp via offset value. f(t) = (initial-zero)*e^(-rate*t) + zero rate = log((initial-zero)/(final-zero))/duration Args: duration (float): Length of time for the ramp to complete initial (float): Initial value of ramp. final (float): Final value of ramp. zero (float): Zero offset of ramp. Returns: func: Function that takes a single parameter `t`. """ rate = 1/duration * log((initial-zero)/(final-zero)) return lambda t: (initial-zero)*exp(-rate*(t)) + zero
[docs] def exp_ramp_t(duration,initial,final,time_constant): """Defines an exponential ramp via time constant. f(t) = (initial-zero)*e^(-t/time_constant) + zero zero = (final-initial*e^(-duration/time_constant))/(1-e^(-duration/time_constant)) Args: duration (float): Length of time for the ramp to complete initial (float): Initial value of ramp. final (float): Final value of ramp. zero (float): Zero offset of ramp. Returns: func: Function that takes a single parameter `t`. """ zero = (final-initial*exp(-duration/time_constant)) / (1-exp(-duration/time_constant)) return lambda t: (initial-zero)*exp(-(t)/time_constant) + zero
[docs] def piecewise_accel(duration,initial,final): """Defines a piecewise acceleration. Args: duration (float): Length of time for the acceleration to complete. initial (float): Initial value. final (float): Final value. """ a = (final-initial) return lambda t: initial + a * ( (9./2 * t**3/duration**3) * (t<duration/3) + (-9*t**3/duration**3 + 27./2*t**2/duration**2 - 9./2*t/duration + 1./2) * (t<2*duration/3)*(t>=duration/3) + (9./2*t**3/duration**3 - 27./2 * t**2/duration**2 + 27./2*t/duration - 7./2) * (t>= 2*duration/3))
[docs] def square_wave(duration, level_0, level_1, frequency, phase, duty_cycle): def square_wave_fixed_parameters(t): # Phase goes from 0 to 1 (NOT 2 pi) over one period. edge_phase_0_to_1 = duty_cycle wrapped_phases = (frequency * t + phase) % 1.0 # Ensure wrapped_phases is an array. wrapped_phases = np.array(wrapped_phases) # Round phases to avoid issues with numerics. Rounding the phase only # changes the output when the phase is just below a threshold where the # output changes values. So if a phase is just below the threshold where # the output changes state (within PHASE_TOLERANCE), round it up so that # the output does change state there. The value of PHASE_TOLERANCE is # based on the fact that labscript internally rounds all times to # multiples of 0.1 ns. LABSCRIPT_TIME_RESOLUTION = 0.1e-9 # 0.1 ns. MIN_PHASE_STEP = frequency * LABSCRIPT_TIME_RESOLUTION PHASE_TOLERANCE = MIN_PHASE_STEP / 2.0 # Round phases near level_0 -> level_1 transition at phase = # edge_phase_0_to_1. is_near_edge = np.isclose( wrapped_phases, edge_phase_0_to_1, rtol=0, atol=PHASE_TOLERANCE, ) wrapped_phases[is_near_edge] = edge_phase_0_to_1 # Round phases near level_1 -> level_0 transition at phase = 1. is_near_edge = np.isclose( wrapped_phases, 1, rtol=0, atol=PHASE_TOLERANCE, ) wrapped_phases[is_near_edge] = 0 # Initialize array to store output values. outputs = np.full_like(t, level_0) # Use boolean indexing to set output to level_1 at the appropriate # times. For example level_0 for phases [0, 0.5) and level_1 for phases # [0.5, 1.0) when duty_cycle is 0.5. level_1_times = (wrapped_phases >= edge_phase_0_to_1) outputs[level_1_times] = level_1 return outputs return square_wave_fixed_parameters
[docs] def pulse_sequence(pulse_sequence,period): """Returns a function that interpolates a pulse sequence. Relies on :obj:`numpy.digitize` to perform the interpolation. Args: pulse_sequence (:obj:`numpy:numpy.ndarray`): 2-D timeseries of change times and associated states. period (float): How long, in seconds, to hold the final state before repeating the sequence. Returns: func: Interpolating function that takes a single parameter `t`. Only well defined if `t` falls within the `pulse_sequence` change times. """ pulse_sequence = np.asarray(sorted(pulse_sequence, key=lambda x: x[0], reverse=True)) pulse_sequence_times = pulse_sequence[:, 0] pulse_sequence_states = pulse_sequence[:, 1] def pulse_function(t): try: len(t) is_array = True except TypeError: t = array([t]) is_array = False times = t % period indices = np.digitize(times, pulse_sequence_times, right=False) states = pulse_sequence_states[indices] if is_array: return states else: return states[0] return pulse_function