Ejemplo n.º 1
0
 def test_MatrixToStateSpace(self):
     """_convertToStateSpace(matrix) gives ss([],[],[],D)"""
     D = np.matrix([[1,2,3],[4,5,6]])
     g = _convertToStateSpace(D)
     def empty(shape):
         m = np.matrix([])
         m.shape = shape
         return m
     np.testing.assert_array_equal(empty((0,0)), g.A)
     np.testing.assert_array_equal(empty((0,D.shape[1])), g.B)
     np.testing.assert_array_equal(empty((D.shape[0],0)), g.C)
     np.testing.assert_array_equal(D,g.D)
Ejemplo n.º 2
0
def ssdata(sys):
    '''
    Return state space data objects for a system

    Parameters
    ----------
    sys: Lti (StateSpace, or TransferFunction)
        LTI system whose data will be returned

    Returns
    -------
    (A, B, C, D): list of matrices
        State space data for the system
    '''
    ss = _convertToStateSpace(sys)
    return (ss.A, ss.B, ss.C, ss.D)
Ejemplo n.º 3
0
 def testMIMO(self):
     """Test conversion of a single input, two-output state-space
     system against the same TF"""
     s = TransferFunction([1, 0], [1])
     b0 = 0.2
     b1 = 0.1
     b2 = 0.5
     a0 = 2.3
     a1 = 6.3
     a2 = 3.6
     a3 = 1.0
     h = (b0 + b1 * s + b2 * s ** 2) / (a0 + a1 * s + a2 * s ** 2 + a3 * s ** 3)
     H = TransferFunction([[h.num[0][0]], [(h * s).num[0][0]]], [[h.den[0][0]], [h.den[0][0]]])
     sys = _convertToStateSpace(H)
     H2 = _convertToTransferFunction(sys)
     np.testing.assert_array_almost_equal(H.num[0][0], H2.num[0][0])
     np.testing.assert_array_almost_equal(H.den[0][0], H2.den[0][0])
     np.testing.assert_array_almost_equal(H.num[1][0], H2.num[1][0])
     np.testing.assert_array_almost_equal(H.den[1][0], H2.den[1][0])
Ejemplo n.º 4
0
 def testAppendTF(self):
     """Test appending a state-space system with a tf"""
     A1 = [[-2, 0.5, 0], [0.5, -0.3, 0], [0, 0, -0.1]]
     B1 = [[0.3, -1.3], [0.1, 0.], [1.0, 0.0]]
     C1 = [[0., 0.1, 0.0], [-0.3, -0.2, 0.0]]
     D1 = [[0., -0.8], [-0.3, 0.]]
     s = TransferFunction([1, 0], [1])
     h = 1/(s+1)/(s+2)
     sys1 = StateSpace(A1, B1, C1, D1)
     sys2 = _convertToStateSpace(h)
     sys3c = sys1.append(sys2)
     np.testing.assert_array_almost_equal(sys1.A, sys3c.A[:3,:3])
     np.testing.assert_array_almost_equal(sys1.B, sys3c.B[:3,:2])
     np.testing.assert_array_almost_equal(sys1.C, sys3c.C[:2,:3])
     np.testing.assert_array_almost_equal(sys1.D, sys3c.D[:2,:2])
     np.testing.assert_array_almost_equal(sys2.A, sys3c.A[3:,3:])
     np.testing.assert_array_almost_equal(sys2.B, sys3c.B[3:,2:])
     np.testing.assert_array_almost_equal(sys2.C, sys3c.C[2:,3:])
     np.testing.assert_array_almost_equal(sys2.D, sys3c.D[2:,2:])
     np.testing.assert_array_almost_equal(sys3c.A[:3,3:], np.zeros( (3, 2)) )
     np.testing.assert_array_almost_equal(sys3c.A[3:,:3], np.zeros( (2, 3)) )
Ejemplo n.º 5
0
def tf2ss(*args):
    """
    Transform a transfer function to a state space system.

    The function accepts either 1 or 2 parameters:
    
    ``tf2ss(sys)``
        Convert a linear system into transfer function form. Always creates
        a new system, even if sys is already a TransferFunction object.
        
    ``tf2ss(num, den)``
        Create a transfer function system from its numerator and denominator
        polynomial coefficients. 
        
        For details see: :func:`tf` 

    Parameters
    ----------
    sys: Lti (StateSpace or TransferFunction)
        A linear system
    num: array_like, or list of list of array_like
        Polynomial coefficients of the numerator
    den: array_like, or list of list of array_like
        Polynomial coefficients of the denominator

    Returns
    -------
    out: StateSpace 
        New linear system in state space form

    Raises
    ------
    ValueError
        if `num` and `den` have invalid or unequal dimensions, or if an
        invalid number of arguments is passed in
    TypeError
        if `num` or `den` are of incorrect type, or if sys is not a
        TransferFunction object

    See Also
    --------
    ss
    tf
    ss2tf

    Examples
    --------
    >>> num = [[[1., 2.], [3., 4.]], [[5., 6.], [7., 8.]]]
    >>> den = [[[9., 8., 7.], [6., 5., 4.]], [[3., 2., 1.], [-1., -2., -3.]]]
    >>> sys1 = tf2ss(num, den)
    
    >>> sys_tf = tf(num, den)
    >>> sys2 = tf2ss(sys_tf) 

    """

    if len(args) == 2 or len(args) == 3:
        # Assume we were given the num, den
        return _convertToStateSpace(TransferFunction(*args))

    elif len(args) == 1:
        sys = args[0]
        if not isinstance(sys, TransferFunction):
            raise TypeError("tf2ss(sys): sys must be a TransferFunction \
object.")
        return _convertToStateSpace(sys)
    else:
        raise ValueError("Needs 1 or 2 arguments; received %i." % len(args))
Ejemplo n.º 6
0
def feedback(sys1, sys2=1, sign=-1):
    """
    Feedback interconnection between two I/O systems.

    Parameters
    ----------
    sys1: scalar, StateSpace, or TransferFunction
        The primary plant.
    sys2: scalar, StateSpace, or TransferFunction
        The feedback plant (often a feedback controller).
    sign: scalar 
        The sign of feedback.  `sign` = -1 indicates negative feedback, and
        `sign` = 1 indicates positive feedback.  `sign` is an optional
        argument; it assumes a value of -1 if not specified.

    Returns
    -------
    out: StateSpace or TransferFunction

    Raises
    ------
    ValueError
        if `sys1` does not have as many inputs as `sys2` has outputs, or if
        `sys2` does not have as many inputs as `sys1` has outputs
    NotImplementedError
        if an attempt is made to perform a feedback on a MIMO TransferFunction
        object

    See Also
    --------
    series
    parallel

    Notes
    -----
    This function is a wrapper for the feedback function in the StateSpace and
    TransferFunction classes.  It calls TransferFunction.feedback if `sys1` is a
    TransferFunction object, and StateSpace.feedback if `sys1` is a StateSpace
    object.  If `sys1` is a scalar, then it is converted to `sys2`'s type, and
    the corresponding feedback function is used.  If `sys1` and `sys2` are both
    scalars, then TransferFunction.feedback is used.
  
    """

    # Check for correct input types.
    if not isinstance(sys1, (int, float, complex, tf.TransferFunction,
        ss.StateSpace)):
        raise TypeError("sys1 must be a TransferFunction or StateSpace object, \
or a scalar.")
    if not isinstance(sys2, (int, float, complex, tf.TransferFunction,
        ss.StateSpace)):
        raise TypeError("sys2 must be a TransferFunction or StateSpace object, \
or a scalar.")

    # If sys1 is a scalar, convert it to the appropriate LTI type so that we can
    # its feedback member function.
    if isinstance(sys1, (int, float, complex)):
        if isinstance(sys2, tf.TransferFunction):
            sys1 = tf._convertToTransferFunction(sys1)
        elif isinstance(sys2, ss.StateSpace):
            sys1 = ss._convertToStateSpace(sys1)
        else: # sys2 is a scalar.
            sys1 = tf._convertToTransferFunction(sys1)
            sys2 = tf._convertToTransferFunction(sys2)

    return sys1.feedback(sys2, sign)
Ejemplo n.º 7
0
def impulse_response(sys, T=None, X0=0., input=0, output=0,
                    transpose=False, **keywords):
    #pylint: disable=W0622
    """Impulse response of a linear system
    
    If the system has multiple inputs or outputs (MIMO), one input and one 
    output have to be selected for the simulation. The parameters `input` 
    and `output` do this. All other inputs are set to 0, all other outputs 
    are ignored.
    
    For information on the **shape** of parameters `T`, `X0` and 
    return values `T`, `yout` see: :ref:`time-series-convention`

    Parameters
    ----------
    sys: StateSpace, TransferFunction
        LTI system to simulate

    T: array-like object, optional
        Time vector (argument is autocomputed if not given)

    X0: array-like object or number, optional
        Initial condition (default = 0)

        Numbers are converted to constant arrays with the correct shape.

    input: int
        Index of the input that will be used in this simulation.

    output: int
        Index of the output that will be used in this simulation.

    transpose: bool
        If True, transpose all input and output arrays (for backward
        compatibility with MATLAB and scipy.signal.lsim)

    **keywords:
        Additional keyword arguments control the solution algorithm for the 
        differential equations. These arguments are passed on to the function
        :func:`lsim`, which in turn passes them on to
        :func:`scipy.integrate.odeint`. See the documentation for
        :func:`scipy.integrate.odeint` for information about these
        arguments.


    Returns
    -------
    T: array
        Time values of the output
    yout: array
        Response of the system
    
    See Also
    --------
    ForcedReponse, initial_response, step_response

    Examples
    --------
    >>> T, yout = impulse_response(sys, T, X0) 
    """
    sys = _convertToStateSpace(sys) 
    sys = _mimo2siso(sys, input, output, warn_conversion=True)
    
    # System has direct feedthrough, can't simulate impulse response numerically
    if np.any(sys.D != 0) and isctime(sys):
        warnings.warn('System has direct feedthrough: ``D != 0``. The infinite '
                      'impulse at ``t=0`` does not appear in the output. \n'
                      'Results may be meaningless!')
    
    # create X0 if not given, test if X0 has correct shape.
    # Must be done here because it is used for computations here.
    n_states = sys.A.shape[0]
    X0 = _check_convert_array(X0, [(n_states,), (n_states,1)],
                              'Parameter ``X0``: \n', squeeze=True)

    # Compute new X0 that contains the impulse
    # We can't put the impulse into U because there is no numerical
    # representation for it (infinitesimally short, infinitely high).
    # See also: http://www.mathworks.com/support/tech-notes/1900/1901.html
    B = np.asarray(sys.B).squeeze()
    new_X0 = B + X0

    # Compute T and U, no checks necessary, they will be checked in lsim
    if T is None:
        T = _default_response_times(sys.A, 100)
    U = np.zeros_like(T)

    T, yout, _xout  = forced_response(sys, T, U, new_X0, \
                          transpose=transpose, **keywords)
    return T, yout
Ejemplo n.º 8
0
def initial_response(sys, T=None, X0=0., input=0, output=0, transpose=False,
                    **keywords):
    #pylint: disable=W0622
    """Initial condition response of a linear system
    
    If the system has multiple inputs or outputs (MIMO), one input and one 
    output have to be selected for the simulation. The parameters `input` 
    and `output` do this. All other inputs are set to 0, all other outputs 
    are ignored.
    
    For information on the **shape** of parameters `T`, `X0` and 
    return values `T`, `yout` see: :ref:`time-series-convention`

    Parameters
    ----------
    sys: StateSpace, or TransferFunction
        LTI system to simulate

    T: array-like object, optional
        Time vector (argument is autocomputed if not given)

    X0: array-like object or number, optional
        Initial condition (default = 0)

        Numbers are converted to constant arrays with the correct shape.

    input: int
        Index of the input that will be used in this simulation.

    output: int
        Index of the output that will be used in this simulation.

    transpose: bool
        If True, transpose all input and output arrays (for backward
        compatibility with MATLAB and scipy.signal.lsim)

    **keywords:
        Additional keyword arguments control the solution algorithm for the 
        differential equations. These arguments are passed on to the function
        :func:`lsim`, which in turn passes them on to
        :func:`scipy.integrate.odeint`. See the documentation for
        :func:`scipy.integrate.odeint` for information about these
        arguments.


    Returns
    -------
    T: array
        Time values of the output
    yout: array
        Response of the system
    
    See Also
    --------
    forced_response, impulse_response, step_response

    Examples
    --------
    >>> T, yout = initial_response(sys, T, X0)
    """
    sys = _convertToStateSpace(sys) 
    sys = _mimo2siso(sys, input, output, warn_conversion=True)

    # Create time and input vectors; checking is done in forced_response(...)
    # The initial vector X0 is created in forced_response(...) if necessary
    if T is None:
        T = _default_response_times(sys.A, 100)
    U = np.zeros_like(T)

    T, yout, _xout = forced_response(sys, T, U, X0, transpose=transpose,
                                    **keywords)
    return T, yout
Ejemplo n.º 9
0
def forced_response(sys, T=None, U=0., X0=0., transpose=False, **keywords):
    """Simulate the output of a linear system.
    
    As a convenience for parameters `U`, `X0`:
    Numbers (scalars) are converted to constant arrays with the correct shape.
    The correct shape is inferred from arguments `sys` and `T`. 
    
    For information on the **shape** of parameters `U`, `T`, `X0` and 
    return values `T`, `yout`, `xout` see: :ref:`time-series-convention`
    
    Parameters
    ----------
    sys: Lti (StateSpace, or TransferFunction)
        LTI system to simulate
        
    T: array-like 
        Time steps at which the input is defined, numbers must be (strictly 
        monotonic) increasing. 
        
    U: array-like or number, optional
        Input array giving input at each time `T` (default = 0).
        
        If `U` is ``None`` or ``0``, a special algorithm is used. This special 
        algorithm is faster than the general algorithm, which is used otherwise.
        
    X0: array-like or number, optional
        Initial condition (default = 0). 

    transpose: bool
        If True, transpose all input and output arrays (for backward
        compatibility with MATLAB and scipy.signal.lsim)
        
    **keywords:
        Additional keyword arguments control the solution algorithm for the 
        differential equations. These arguments are passed on to the function
        :func:`scipy.integrate.odeint`. See the documentation for
        :func:`scipy.integrate.odeint` for information about these
        arguments.

    Returns
    -------
    T: array
        Time values of the output. 
    yout: array
        Response of the system. 
    xout: array
        Time evolution of the state vector. 
    
    See Also
    --------
    step_response, initial_response, impulse_response
    
    Examples
    --------
    >>> T, yout, xout = forced_response(sys, T, u, X0)
    """
    if not isinstance(sys, Lti):
        raise TypeError('Parameter ``sys``: must be a ``Lti`` object. '
                        '(For example ``StateSpace`` or ``TransferFunction``)')
    sys = _convertToStateSpace(sys) 
    A, B, C, D = np.asarray(sys.A), np.asarray(sys.B), np.asarray(sys.C), \
                 np.asarray(sys.D)
#    d_type = A.dtype
    n_states = A.shape[0]
    n_inputs = B.shape[1]

    # Set and/or check time vector in discrete time case
    if isdtime(sys, strict=True):
        if T == None:
            if U == None:
                raise ValueError('Parameters ``T`` and ``U`` can\'t both be'
                                 'zero for discrete-time simulation')
            # Set T to integers with same length as U
            T = range(len(U))
        else:
            # Make sure the input vector and time vector have same length
            # TODO: allow interpolation of the input vector
            if len(U) != len(T):
                ValueError('Pamameter ``T`` must have same length as'
                           'input vector ``U``')

    # Test if T has shape (n,) or (1, n);
    # T must be array-like and values must be increasing.
    # The length of T determines the length of the input vector.
    if T is None:
        raise ValueError('Parameter ``T``: must be array-like, and contain '
                         '(strictly monotonic) increasing numbers.')
    T = _check_convert_array(T, [('any',), (1,'any')], 
                             'Parameter ``T``: ', squeeze=True, 
                             transpose = transpose)
    if not all(T[1:] - T[:-1] > 0):
        raise ValueError('Parameter ``T``: time values must be '
                         '(strictly monotonic) increasing numbers.')
    n_steps = len(T)            # number of simulation steps
    
    #create X0 if not given, test if X0 has correct shape
    X0 = _check_convert_array(X0, [(n_states,), (n_states,1)], 
                              'Parameter ``X0``: ', squeeze=True)

    # Separate out the discrete and continuous time cases
    if isctime(sys):
        # Solve the differential equation, copied from scipy.signal.ltisys.
        dot, squeeze, = np.dot, np.squeeze #Faster and shorter code

        # Faster algorithm if U is zero
        if U is None or (isinstance(U, (int, float)) and U == 0):
            # Function that computes the time derivative of the linear system
            def f_dot(x, _t):
                return dot(A,x)
        
            xout = sp.integrate.odeint(f_dot, X0, T, **keywords)
            yout = dot(C, xout.T)

        # General algorithm that interpolates U in between output points
        else:
            # Test if U has correct shape and type
            legal_shapes = [(n_steps,), (1,n_steps)] if n_inputs == 1 else \
                           [(n_inputs, n_steps)]
            U = _check_convert_array(U, legal_shapes,
                                     'Parameter ``U``: ', squeeze=False,
                                     transpose=transpose)
            # convert 1D array to D2 array with only one row
            if len(U.shape) == 1:
                U = U.reshape(1,-1)                      #pylint: disable=E1103

            # Create a callable that uses linear interpolation to
            # calculate the input at any time.
            compute_u = \
                sp.interpolate.interp1d(T, U, kind='linear', copy=False,
                                        axis=-1, bounds_error=False,
                                        fill_value=0)
        
            # Function that computes the time derivative of the linear system
            def f_dot(x, t):
                return dot(A,x) + squeeze(dot(B,compute_u([t])))
        
            xout = sp.integrate.odeint(f_dot, X0, T, **keywords)
            yout = dot(C, xout.T) + dot(D, U)

        yout = squeeze(yout)
        xout = xout.T

    else:
        # Discrete time simulation using signal processing toolbox
        dsys = (A, B, C, D, sys.dt)
        tout, yout, xout = sp.signal.dlsim(dsys, U, T, X0)

    # See if we need to transpose the data back into MATLAB form
    if (transpose):
        T = np.transpose(T)
        yout = np.transpose(yout)
        xout = np.transpose(xout)

    return T, yout, xout
Ejemplo n.º 10
0
 def test_zero_empty(self):
     """Test to make sure zero() works with no zeros in system."""
     sys = _convertToStateSpace(TransferFunction([1], [1, 2, 1]))
     np.testing.assert_array_equal(sys.zero(), np.array([]))
Ejemplo n.º 11
0
import numpy as np
from control.matlab import tf
from matplotlib import pyplot as plot
from numpy.linalg import lstsq
from control.statesp import _convertToStateSpace
# Exact Process Model
K = 10

numer = [1]
denom = [5,1]
sys1 = tf(numer,denom)
#denominator = tf([[5,1+K]])

#Transformation to State-Space

sys = _convertToStateSpace(sys1)
A, B, C, D = np.asarray(sys.A), np.asarray(sys.B), np.asarray(sys.C), \
    np.asarray(sys.D)

Nstates = A.shape[0]
z = np.zeros((Nstates, 1))

t_start = 0
t_end = 30
dt = 0.05
tspan = np.arange(t_start, t_end, dt)
npoints = len(tspan)

#List for storages
yplot = np.zeros(npoints)
ym_plot = np.zeros(npoints)
Ejemplo n.º 12
0
def forced_response(sys, T=None, U=0., X0=0., transpose=False, **keywords):
    """Simulate the output of a linear system.
    
    As a convenience for parameters `U`, `X0`:
    Numbers (scalars) are converted to constant arrays with the correct shape.
    The correct shape is inferred from arguments `sys` and `T`. 
    
    For information on the **shape** of parameters `U`, `T`, `X0` and 
    return values `T`, `yout`, `xout` see: :ref:`time-series-convention`
    
    Parameters
    ----------
    sys: Lti (StateSpace, or TransferFunction)
        LTI system to simulate
        
    T: array-like 
        Time steps at which the input is defined, numbers must be (strictly 
        monotonic) increasing. 
        
    U: array-like or number, optional
        Input array giving input at each time `T` (default = 0).
        
        If `U` is ``None`` or ``0``, a special algorithm is used. This special 
        algorithm is faster than the general algorithm, which is used otherwise.
        
    X0: array-like or number, optional
        Initial condition (default = 0). 

    transpose: bool
        If True, transpose all input and output arrays (for backward
        compatibility with MATLAB and scipy.signal.lsim)
        
    **keywords:
        Additional keyword arguments control the solution algorithm for the 
        differential equations. These arguments are passed on to the function
        :func:`scipy.integrate.odeint`. See the documentation for
        :func:`scipy.integrate.odeint` for information about these
        arguments.

    Returns
    -------
    T: array
        Time values of the output. 
    yout: array
        Response of the system. 
    xout: array
        Time evolution of the state vector. 
    
    See Also
    --------
    step_response, initial_response, impulse_response
    
    Examples
    --------
    >>> T, yout, xout = forced_response(sys, T, u, X0)
    """
    if not isinstance(sys, Lti):
        raise TypeError('Parameter ``sys``: must be a ``Lti`` object. '
                        '(For example ``StateSpace`` or ``TransferFunction``)')
    sys = _convertToStateSpace(sys)
    A, B, C, D = np.asarray(sys.A), np.asarray(sys.B), np.asarray(sys.C), \
                 np.asarray(sys.D)
    #    d_type = A.dtype
    n_states = A.shape[0]
    n_inputs = B.shape[1]

    # Set and/or check time vector in discrete time case
    if isdtime(sys, strict=True):
        if T == None:
            if U == None:
                raise ValueError('Parameters ``T`` and ``U`` can\'t both be'
                                 'zero for discrete-time simulation')
            # Set T to integers with same length as U
            T = range(len(U))
        else:
            # Make sure the input vector and time vector have same length
            # TODO: allow interpolation of the input vector
            if len(U) != len(T):
                ValueError('Pamameter ``T`` must have same length as'
                           'input vector ``U``')

    # Test if T has shape (n,) or (1, n);
    # T must be array-like and values must be increasing.
    # The length of T determines the length of the input vector.
    if T is None:
        raise ValueError('Parameter ``T``: must be array-like, and contain '
                         '(strictly monotonic) increasing numbers.')
    T = _check_convert_array(T, [('any', ), (1, 'any')],
                             'Parameter ``T``: ',
                             squeeze=True,
                             transpose=transpose)
    if not all(T[1:] - T[:-1] > 0):
        raise ValueError('Parameter ``T``: time values must be '
                         '(strictly monotonic) increasing numbers.')
    n_steps = len(T)  # number of simulation steps

    #create X0 if not given, test if X0 has correct shape
    X0 = _check_convert_array(X0, [(n_states, ), (n_states, 1)],
                              'Parameter ``X0``: ',
                              squeeze=True)

    # Separate out the discrete and continuous time cases
    if isctime(sys):
        # Solve the differential equation, copied from scipy.signal.ltisys.
        dot, squeeze, = np.dot, np.squeeze  #Faster and shorter code

        # Faster algorithm if U is zero
        if U is None or (isinstance(U, (int, float)) and U == 0):
            # Function that computes the time derivative of the linear system
            def f_dot(x, _t):
                return dot(A, x)

            xout = sp.integrate.odeint(f_dot, X0, T, **keywords)
            yout = dot(C, xout.T)

        # General algorithm that interpolates U in between output points
        else:
            # Test if U has correct shape and type
            legal_shapes = [(n_steps,), (1,n_steps)] if n_inputs == 1 else \
                           [(n_inputs, n_steps)]
            U = _check_convert_array(U,
                                     legal_shapes,
                                     'Parameter ``U``: ',
                                     squeeze=False,
                                     transpose=transpose)
            # convert 1D array to D2 array with only one row
            if len(U.shape) == 1:
                U = U.reshape(1, -1)  #pylint: disable=E1103

            # Create a callable that uses linear interpolation to
            # calculate the input at any time.
            compute_u = \
                sp.interpolate.interp1d(T, U, kind='linear', copy=False,
                                        axis=-1, bounds_error=False,
                                        fill_value=0)

            # Function that computes the time derivative of the linear system
            def f_dot(x, t):
                return dot(A, x) + squeeze(dot(B, compute_u([t])))

            xout = sp.integrate.odeint(f_dot, X0, T, **keywords)
            yout = dot(C, xout.T) + dot(D, U)

        yout = squeeze(yout)
        xout = xout.T

    else:
        # Discrete time simulation using signal processing toolbox
        dsys = (A, B, C, D, sys.dt)
        tout, yout, xout = sp.signal.dlsim(dsys, U, T, X0)

    # See if we need to transpose the data back into MATLAB form
    if (transpose):
        T = np.transpose(T)
        yout = np.transpose(yout)
        xout = np.transpose(xout)

    return T, yout, xout