Example #1
0
def qmult(p0, pvec, q0, qvec):
    """
    Quaternion Multiplications r = p x q
    
    Parameters
    ----------
    p0, q0 : {(N,)} array_like 
        Scalar componenet of the quaternion
    pvec, qvec : {(N,3)} array_like
        Vector component of the quaternion
    
    Returns
    -------
    r0 : {(N,)} array like scalar componenet of the quaternion
    rvec : {(N,3)} array like vector component of the quaternion
    
    Examples
    --------
    >>> import numpy as np
    >>> from navpy import qmult
    >>> p0, pvec = 0.701057, np.array([-0.69034553,  0.15304592,  0.09229596])
    >>> q0, qvec = 0.987228, np.array([ 0.12613659,  0.09199968,  0.03171637])
    >>> qmult(q0,qvec,p0,pvec)
    (0.76217346258977192, array([-0.58946236,  0.18205109,  0.1961684 ]))
    >>> s0, svec = 0.99879, np.array([ 0.02270747,  0.03430854, -0.02691584])
    >>> t0, tvec = 0.84285, np.array([ 0.19424161, -0.18023625, -0.46837843])
    >>> qmult(s0,svec,t0,tvec)
    (0.83099625967941704, array([ 0.19222498, -0.1456937 , -0.50125456]))
    >>> qmult([p0, s0],[pvec, svec],[q0, t0], [qvec, tvec])
    (array([ 0.76217346,  0.83099626]), array([[-0.59673664,  0.24912539,  0.03053588], [ 0.19222498, -0.1456937 , -0.50125456]]))
    """

    p0, Np = _input_check_Nx1(p0)
    q0, Nq = _input_check_Nx1(q0)
    if (Np != Nq):
        raise ValueError('Inputs are not of the same dimension')

    pvec, Np = _input_check_Nx3(pvec)
    if (Np != Nq):
        raise ValueError('Inputs are not of the same dimension')

    qvec, Nq = _input_check_Nx3(qvec)
    if (Np != Nq):
        raise ValueError('Inputs are not of the same dimension')

    if (Np > 1):
        r0 = p0 * q0 - np.sum(pvec * qvec, axis=1)
    else:
        r0 = p0 * q0 - np.dot(pvec, qvec)

    rvec = p0.reshape(Np, 1) * qvec + q0.reshape(Np, 1) * pvec + np.cross(
        pvec, qvec)

    # For only 1-D input, make it into a flat 1-D array
    if (Np == 1):
        rvec = rvec.reshape(3)

    return r0, rvec
Example #2
0
def navrate(VN, VE, VD, lat, alt, lat_unit='deg', alt_unit='m', model='wgs84'):
    """
    Calculate navigation/transport rate given VN, VE, VD, lat, and alt.
    Navigation/transport rate is the angular velocity of the NED frame relative
    to the earth ECEF frame. 
    Paul Groves's Notation: :math:`\omega_{EN}^N`, Eq. (5.37), Ch. 5.3, pp. 131
    
    References
    ----------
    P. Groves, GNSS, Inertial, and Integrated Navigation Systems, Artech House, 2008
    
    Parameters
    ----------
    VN: {(N,)} array like earth relative velocity in the North direction, m/s
    VE: {(N,)} array like earth relative velocity in the East direction, m/s
    VD: {(N,)} array like earth relative velocity in the Down direction, m/s
    lat: {(N,)} array like latitudes, unit specified in lat_unit, default deg
    alt: {(N,)} array like altitudes, unit specified in alt_unit, default m
    
    Returns
    -------
    rho: {(N,3)} np.array of the transport rate.
    The unit is in rad/seconds.
    
    Calls
    -----
    earthrad
    """

    lat, N1 = _input_check_Nx1(lat)
    alt, N2 = _input_check_Nx1(alt)
    VN, N3 = _input_check_Nx1(VN)
    VE, N4 = _input_check_Nx1(VE)
    VD, N5 = _input_check_Nx1(VD)

    if ((N1 != N2) or (N2 != N3) or (N1 != N3) or (N1 != N4) or (N4 != N5)):
        raise ValueError('Inputs are not of the same dimension')

    Rew, Rns = earthrad(lat, lat_unit=lat_unit)

    rho = np.zeros((N1, 3))

    if (model == 'wgs84'):
        rho[:, 0] = VE / (Rew + alt)
        rho[:, 1] = -VN / (Rns + alt)
        rho[:, 2] = -VE * np.tan(np.deg2rad(lat)) / (Rew + alt)
    else:
        raise ValueError('Model unknown')

    if (N1 == 1):
        rho = rho.reshape(3)

    return rho
Example #3
0
def skew(w, output_type='ndarray'):
    """
    Make a skew symmetric 2-D array
    
    Parameters
    ----------
    w : {(3,)} array_like
    
    Returns
    -------
    C : {(3,3)} skew symmetric representation of w
    
    Examples
    --------
    >>> import numpy as np
    >>> from navpy import skew
    >>> w = [1, 2, 3]
    >>> skew(w)
    array([[ 0., -3.,  2.],
           [ 3.,  0., -1.],
           [-2.,  1.,  0.]])
    """
    w, N = _input_check_Nx1(np.array(w))
    if (N != 3):
        raise ValueError('Input dimension is not 3')

    C = np.array([[0.0, -w[2], w[1]], [w[2], 0.0, -w[0]], [-w[1], w[0], 0.0]])
    if (output_type != 'ndarray'):
        C = np.matrix(C)

    return C
Example #4
0
def wrapToPi(e):
    """
    Wraping angle to [-pi,pi] interval
    """
    dum, N = _input_check_Nx1(e)

    ew = np.mod(e + np.pi, 2 * np.pi) - np.pi

    return ew
Example #5
0
def lla2ecef(lat, lon, alt, latlon_unit='deg', alt_unit='m', model='wgs84'):
    """
    Convert Latitude, Longitude, Altitude, to ECEF position
    
    Parameters
    ----------
    lat : {(N,)} array like latitude, unit specified by latlon_unit, default in deg
    lon : {(N,)} array like longitude, unit specified by latlon_unit, default in deg
    alt : {(N,)} array like altitude, unit specified by alt_unit, default in m
    
    Returns
    -------
    ecef : {(N,3)} array like ecef position, unit is the same as alt_unit
    """
    lat, N1 = _input_check_Nx1(lat)
    lon, N2 = _input_check_Nx1(lon)
    alt, N3 = _input_check_Nx1(alt)

    if ((N1 != N2) or (N2 != N3) or (N1 != N3)):
        raise ValueError('Inputs are not of the same dimension')

    if (model == 'wgs84'):
        Rew, Rns = earthrad(lat, lat_unit=latlon_unit)
    else:
        Rew = wgs84.a

    if (latlon_unit == 'deg'):
        lat = np.deg2rad(lat)
        lon = np.deg2rad(lon)

    x = (Rew + alt) * np.cos(lat) * np.cos(lon)
    y = (Rew + alt) * np.cos(lat) * np.sin(lon)
    z = ((1 - wgs84._ecc_sqrd) * Rew + alt) * np.sin(lat)

    ecef = np.vstack((x, y, z)).T

    if (N1 == 1):
        ecef = ecef.reshape(3)

    return ecef
Example #6
0
def earthrate(lat, lat_unit='deg', model='wgs84'):
    """
    Calculate the earth rotation rate resolved on NED axes 
    given VN, VE, VD, lat, and alt.
    
    Paul Groves's Notation: :math:`\omega_{IE}^N`, Eq. (2.75), Ch. 2.3, pp. 44
    
    Parameters
    ----------
    lat : {(N,)} array like latitudes, unit specified in lat_unit, default deg
    
    Returns
    -------
    e : {(N,3)} np.array of the earth's rotation rate
        The unit is in rad/seconds.

    References
    ----------
    [1] P. Groves, GNSS, Inertial, and Integrated Navigation Systems, Artech House, 2008
    """
    if (lat_unit == 'deg'):
        lat = np.deg2rad(lat)
    elif (lat_unit == 'rad'):
        pass
    else:
        raise ValueError('Input unit unknown')

    lat, N = _input_check_Nx1(lat)

    e = np.zeros((N, 3))
    if (model == 'wgs84'):
        e[:, 0] = wgs84.omega_E * np.cos(lat)
        e[:, 1] = -wgs84.omega_E * np.sin(lat)
    else:
        raise ValueError('Model unknown')

    if (N == 1):
        e = e.reshape(3)

    return e
Example #7
0
def llarate(VN, VE, VD, lat, alt, lat_unit='deg', alt_unit='m'):
    """
    Calculate Latitude, Longitude, Altitude Rate given locally tangent velocity
    
    Parameters
    ----------
    VN : {(N,)} array like earth relative velocity in the North direction, m/s
    VE : {(N,)} array like earth relative velocity in the East direction, m/s
    VD : {(N,)} array like earth relative velocity in the Down direction, m/s
    lat : {(N,)} array like latitudes, unit specified in lat_unit, default deg
    alt : {(N,)} array like altitudes, unit specified in alt_unit, default m
    
    Returns
    -------
    lla_dot : {(N,3)} np.array of latitude rate, longitude rate, altitude rate.
              The unit of latitude and longitude rate will be the same as the 
              unit specified by lat_unit and the unit of altitude rate will be 
              the same as alt_unit
    
    See Also
    --------
    earthrad : called by this method
    
    Examples
    --------
    >>> import numpy as np
    >>> from navpy import llarate
    >>> llarate(100,0,0,45.0,0) # Moving North at 100 m/s, location is at N45.0
    array([ 0.00089983,  0.        ,  0.        ])
    >>> # That output was in deg/sec
    >>> lat = [np.pi/4, -np.pi/6]
    >>> alt = [100.0, 50]
    >>> VN = [100, 0]
    >>> VE = [0, 100]
    >>> VD = [0, -5]
    >>> llarate(VN,VE,VD,lat,alt,lat_unit='rad')
    array([[  1.57047955e-05,   0.00000000e+00,   0.00000000e+00],\
           [  0.00000000e+00,   1.80887436e-05,   5.00000000e+00]])
    >>> # That output was in rad/sec
    """
    dim_check = 1
    VN, N1 = _input_check_Nx1(VN)
    VE, N2 = _input_check_Nx1(VE)
    if (N2 != N1):
        dim_check *= 0
    VD, N2 = _input_check_Nx1(VD)
    if (N2 != N1):
        dim_check *= 0
    lat, N2 = _input_check_Nx1(lat)
    if (N2 != N1):
        dim_check *= 0
    alt, N2 = _input_check_Nx1(alt)
    if (N2 != N1):
        dim_check *= 0
    if (dim_check == 0):
        raise ValueError('Inputs are not of the same dimension')

    Rew, Rns = earthrad(lat, lat_unit=lat_unit)

    lla_dot = np.zeros((N1, 3))
    if (lat_unit == 'deg'):
        lla_dot[:, 0] = np.rad2deg(VN / (Rns + alt))
        lla_dot[:, 1] = np.rad2deg(VE / (Rew + alt) / np.cos(np.deg2rad(lat)))
        lla_dot[:, 2] = -VD
    elif (lat_unit == 'rad'):
        lla_dot[:, 0] = VN / (Rns + alt)
        lla_dot[:, 1] = VE / (Rew + alt) / np.cos(lat)
        lla_dot[:, 2] = -VD

    if (N1 == 1):
        lla_dot = lla_dot.reshape(3)

    return lla_dot
Example #8
0
def quat2dcm(q0, qvec, rotation_sequence='ZYX', output_type='ndarray'):
    """
    Convert a single unit quaternion to one DCM
    
    Parameters
    ----------
    q0 : {(N,), (N,1), or (1,N)} array_like 
        Scalar componenet of the quaternion
    qvec : {(N,3),(3,N)} array_like 
        Vector component of the quaternion
    rotation_sequence : {'ZYX'}, optional
        Rotation sequences. Default is 'ZYX'.
    output_type : {'ndarray','matrix'}, optional
        Output is either numpy array (default) or numpy matrix.
            
    Returns
    -------
    C_N2B : direction consine matrix that rotates the vector from the first frame
            to the second frame according to the specified rotation_sequence.
        
    Examples
    --------
    >>> import numpy as np
    >>> from navpy import quat2dcm
    >>> q0 = 1
    >>> qvec = [0, 0, 0]
    >>> C = quat2dcm(q0,qvec)
    >>> C
    array([[ 1.,  0.,  0.],
           [ 0.,  1.,  0.],
           [ 0.,  0.,  1.]])
    
    >>> q0 = 0.9811
    >>> qvec = np.array([-0.0151, 0.0858, 0.1730])
    >>> C = quat2dcm(q0,qvec,output_type='matrix')
    >>> C
    matrix([[  9.25570440e-01,   3.36869440e-01,  -1.73581360e-01],
            [ -3.42051760e-01,   9.39837700e-01,   5.75800000e-05],
            [  1.63132160e-01,   5.93160200e-02,   9.84972420e-01]])
    """
    # Input check
    q0, N0 = _input_check_Nx1(q0)
    qvec, Nvec = _input_check_Nx3(qvec)

    if ((N0 != 1) | (Nvec != 1)):
        raise ValueError('Can only process 1 quaternion')

    q1 = qvec[0]
    q2 = qvec[1]
    q3 = qvec[2]
    if (rotation_sequence == 'ZYX'):
        C_N2B = np.zeros((3, 3))

        C_N2B[0, 0] = 2 * q0**2 - 1 + 2 * q1**2
        C_N2B[1, 1] = 2 * q0**2 - 1 + 2 * q2**2
        C_N2B[2, 2] = 2 * q0**2 - 1 + 2 * q3**2

        C_N2B[0, 1] = 2 * q1 * q2 + 2 * q0 * q3
        C_N2B[0, 2] = 2 * q1 * q3 - 2 * q0 * q2

        C_N2B[1, 0] = 2 * q1 * q2 - 2 * q0 * q3
        C_N2B[1, 2] = 2 * q2 * q3 + 2 * q0 * q1

        C_N2B[2, 0] = 2 * q1 * q3 + 2 * q0 * q2
        C_N2B[2, 1] = 2 * q2 * q3 - 2 * q0 * q1
    else:
        raise ValueError('rotation_sequence unknown')

    if (output_type == 'matrix'):
        C_N2B = np.asmatrix(C_N2B)

    return C_N2B
Example #9
0
def quat2angle(q0, qvec, output_unit='rad', rotation_sequence='ZYX'):
    """
    Convert a unit quaternion to the equivalent sequence of angles of rotation
    about the rotation_sequence axes.
    
    This function can take inputs in either degree or radians, and can also
    batch process a series of rotations (e.g., time series of quaternions).
    By default this function assumes aerospace rotation sequence but can be
    changed using the ``rotation_sequence`` keyword argument.
    
    Parameters
    ----------
    q0 : {(N,), (N,1), or (1,N)} array_like 
        Scalar componenet of the quaternion
    qvec : {(N,3),(3,N)} array_like 
        Vector component of the quaternion
    rotation_sequence : {'ZYX'}, optional
        Rotation sequences. Default is 'ZYX'.

    Returns
    -------
    rotAngle1, rotAngle2, rotAngle3 : {(N,), (N,1), or (1,N)} array_like
        They are a sequence of angles about successive axes described by
        rotation_sequence.
    output_unit : {'rad', 'deg'}, optional
        Rotation angles. Default is 'rad'.
    
    Notes
    -----
    Convert rotation angles to unit quaternion that transforms a vector in F1 to
    F2 according to
    
    :math:`v_q^{F2} = q^{-1} \otimes v_q^{F1} \otimes q`
    
    where :math:`\otimes` indicates the quaternion multiplcation and :math:`v_q^F`
    is a pure quaternion representation of the vector :math:`v_q^F`. The scalar
    componenet of :math:`v_q^F` is zero.
    For aerospace sequence ('ZYX'): rotAngle1 = psi, rotAngle2 = the, 
    and rotAngle3 = phi
    
    Examples
    --------
    >>> import numpy as np
    >>> from navpy import quat2angle
    >>> q0 = 0.800103145191266
    >>> qvec = np.array([0.4619398,0.3314136,-0.1913417])
    >>> psi, theta, phi = quat2angle(q0,qvec)
    >>> psi
    1.0217702360987295e-07
    >>> theta
    0.7853982192745731
    >>> phi
    1.0471976051067484
    
    >>> psi, theta, phi = quat2angle(q0,qvec,output_unit='deg')
    >>> psi
    5.8543122160542875e-06
    >>> theta
    45.00000320152342
    >>> phi
    60.000003088824108
    
    >>> q0 = [ 0.96225019,  0.92712639,  0.88162808]
    >>> qvec = np.array([[-0.02255757,  0.25783416,  0.08418598],\
                         [-0.01896854,  0.34362114,  0.14832854],\
                         [-0.03266701,  0.4271086 ,  0.19809857]])
    >>> psi, theta, phi = quat2angle(q0,qvec,output_unit='deg')
    >>> psi
    array([  9.99999941,  19.99999997,  29.9999993 ])
    >>> theta
    array([ 30.00000008,  39.99999971,  50.00000025])
    >>> phi
    array([ -6.06200867e-07,   5.00000036e+00,   1.00000001e+01])
    """
    q0, N0 = _input_check_Nx1(q0)
    qvec, Nvec = _input_check_Nx3(qvec)

    if (N0 != Nvec):
        raise ValueError('Inputs are not of same dimensions')
    if (N0 == 1):
        q1 = qvec[0]
        q2 = qvec[1]
        q3 = qvec[2]
    else:
        q1 = qvec[:, 0]
        q2 = qvec[:, 1]
        q3 = qvec[:, 2]

    rotAngle1 = np.zeros(N0)
    rotAngle2 = np.zeros(N0)
    rotAngle3 = np.zeros(N0)

    if (rotation_sequence == 'ZYX'):
        m11 = 2 * q0**2 + 2 * q1**2 - 1
        m12 = 2 * q1 * q2 + 2 * q0 * q3
        m13 = 2 * q1 * q3 - 2 * q0 * q2
        m23 = 2 * q2 * q3 + 2 * q0 * q1
        m33 = 2 * q0**2 + 2 * q3**2 - 1

        rotAngle1 = np.arctan2(m12, m11)
        rotAngle2 = np.arcsin(-m13)
        rotAngle3 = np.arctan2(m23, m33)
    else:
        raise ValueError('rotation_sequence unknown')

    if (output_unit == 'deg'):
        rotAngle1 = np.rad2deg(rotAngle1)
        rotAngle2 = np.rad2deg(rotAngle2)
        rotAngle3 = np.rad2deg(rotAngle3)

    return rotAngle1, rotAngle2, rotAngle3
Example #10
0
def angle2quat(rotAngle1,
               rotAngle2,
               rotAngle3,
               input_unit='rad',
               rotation_sequence='ZYX'):
    """
    Convert a sequence of rotation angles to an equivalent unit quaternion
    
    This function can take inputs in either degree or radians, and can also 
    batch process a series of rotations (e.g., time series of Euler angles).
    By default this function assumes aerospace rotation sequence but can be 
    changed using the ``rotation_sequence`` keyword argument.
    
    Parameters
    ----------
    rotAngle1, rotAngle2, rotAngle3 : {(N,), (N,1), or (1,N)}
        They are a sequence of angles about successive axes described by rotation_sequence.
    input_unit : {'rad', 'deg'}, optional
        Rotation angles. Default is 'rad'.
    rotation_sequence : {'ZYX'}, optional 
        Rotation sequences. Default is 'ZYX'.
    
    Returns
    -------
    q0 : {(N,)} array like scalar componenet of the quaternion
    qvec : {(N,3)} array like vector component of the quaternion
    
    Notes
    -----
    Convert rotation angles to unit quaternion that transforms a vector in F1 to
    F2 according to
    
    :math:`v_q^{F2} = q^{-1} \otimes v_q^{F1} \otimes q`
    
    where :math:`\otimes` indicates the quaternion multiplcation and :math:`v_q^F`
    is a pure quaternion representation of the vector :math:`v_q^F`. The scalar
    componenet of :math:`v_q^F` is zero.
    For aerospace sequence ('ZYX'): rotAngle1 = psi, rotAngle2 = the,
    and rotAngle3 = phi
    
    Examples
    --------
    >>> import numpy as np
    >>> from navpy import angle2quat
    >>> psi = 0
    >>> theta = np.pi/4.0
    >>> phi = np.pi/3.0
    >>> q0, qvec = angle2quat(psi,theta,phi)
    >>> q0
    0.80010314519126557
    >>> qvec
    array([ 0.46193977,  0.33141357, -0.19134172])
    
    >>> psi = [10, 20, 30]
    >>> theta = [30, 40, 50]
    >>> phi = [0, 5, 10]
    >>> q0, qvec = angle2quat(psi,theta,phi,input_unit = 'deg')
    >>> q0
    array([ 0.96225019,  0.92712639,  0.88162808])
    >>> qvec
    array([[-0.02255757,  0.25783416,  0.08418598],
           [-0.01896854,  0.34362114,  0.14832854],
           [-0.03266701,  0.4271086 ,  0.19809857]])
    """

    # INPUT CHECK
    rotAngle1, N1 = _input_check_Nx1(rotAngle1)
    rotAngle2, N2 = _input_check_Nx1(rotAngle2)
    rotAngle3, N3 = _input_check_Nx1(rotAngle3)

    if ((N1 != N2) | (N1 != N3) | (N2 != N3)):
        raise ValueError('Inputs are not of same dimensions')

    q0 = np.zeros(N1)
    qvec = np.zeros((N1, 3))

    if (input_unit == 'deg'):
        rotAngle1 = np.deg2rad(rotAngle1)
        rotAngle2 = np.deg2rad(rotAngle2)
        rotAngle3 = np.deg2rad(rotAngle3)

    rotAngle1 /= 2.0
    rotAngle2 /= 2.0
    rotAngle3 /= 2.0

    if (rotation_sequence == 'ZYX'):
        q0[:] = np.cos(rotAngle1)*np.cos(rotAngle2)*np.cos(rotAngle3) + \
                np.sin(rotAngle1)*np.sin(rotAngle2)*np.sin(rotAngle3)

        qvec[:,0] = np.cos(rotAngle1)*np.cos(rotAngle2)*np.sin(rotAngle3) - \
            np.sin(rotAngle1)*np.sin(rotAngle2)*np.cos(rotAngle3)

        qvec[:,1] = np.cos(rotAngle1)*np.sin(rotAngle2)*np.cos(rotAngle3) + \
            np.sin(rotAngle1)*np.cos(rotAngle2)*np.sin(rotAngle3)

        qvec[:,2] = np.sin(rotAngle1)*np.cos(rotAngle2)*np.cos(rotAngle3) - \
            np.cos(rotAngle1)*np.sin(rotAngle2)*np.sin(rotAngle3)
    else:
        raise ValueError('rotation_sequence unknown')

    if (N1 == 1):
        q0 = q0[0]
        qvec = qvec.reshape(3, )
    return q0, qvec
Example #11
0
def angle2dcm(rotAngle1,
              rotAngle2,
              rotAngle3,
              input_unit='rad',
              rotation_sequence='ZYX',
              output_type='ndarray'):
    """
    This function converts Euler Angle into Direction Cosine Matrix (DCM).
    The DCM is described by three sucessive rotation rotAngle1, rotAngle2, and
    rotAngle3 about the axes described by the rotation_sequence.

    The default rotation_sequence='ZYX' is the aerospace sequence and rotAngle1
    is the yaw angle, rotAngle2 is the pitch angle, and rotAngle3 is the roll
    angle. In this case DCM transforms a vector from the locally level
    coordinate frame (i.e. the NED frame) to the body frame.

    This function can batch process a series of rotations (e.g., time series
    of Euler angles).

    Parameters
    ----------
    rotAngle1, rotAngle2, rotAngle3 : angles {(N,), (N,1), or (1,N)}
            They are a sequence of angles about successive axes described by
            rotation_sequence.
    input_unit : {'rad', 'deg'}, optional
            Rotation angles. Default is 'rad'.
    rotation_sequence : {'ZYX'}, optional
            Rotation sequences. Default is 'ZYX'.
    output_type : {'ndarray','matrix'}, optional
            Output type. Default is 'ndarray'.

    Returns
    --------
    C : {3x3} Direction Cosine Matrix

    Notes
    -----
    Programmer:    Adhika Lie
    Created:    	 May 03, 2011
    Last Modified: January 12, 2016
    """
    rotAngle1, N1 = _input_check_Nx1(rotAngle1)
    rotAngle2, N2 = _input_check_Nx1(rotAngle2)
    rotAngle3, N3 = _input_check_Nx1(rotAngle3)

    if (N1 != N2 or N1 != N3):
        raise ValueError('Inputs are not of same dimensions')
    if (N1 > 1 and output_type != 'ndarray'):
        raise ValueError('Matrix output requires scalar inputs')

    R3 = np.zeros((N1, 3, 3))
    R2 = np.zeros((N1, 3, 3))
    R1 = np.zeros((N1, 3, 3))

    if (input_unit == 'deg'):
        rotAngle1 = np.deg2rad(rotAngle1)
        rotAngle2 = np.deg2rad(rotAngle2)
        rotAngle3 = np.deg2rad(rotAngle3)

    R3[:, 2, 2] = 1.0
    R3[:, 0, 0] = np.cos(rotAngle1)
    R3[:, 0, 1] = np.sin(rotAngle1)
    R3[:, 1, 0] = -np.sin(rotAngle1)
    R3[:, 1, 1] = np.cos(rotAngle1)

    R2[:, 1, 1] = 1.0
    R2[:, 0, 0] = np.cos(rotAngle2)
    R2[:, 0, 2] = -np.sin(rotAngle2)
    R2[:, 2, 0] = np.sin(rotAngle2)
    R2[:, 2, 2] = np.cos(rotAngle2)

    R1[:, 0, 0] = 1.0
    R1[:, 1, 1] = np.cos(rotAngle3)
    R1[:, 1, 2] = np.sin(rotAngle3)
    R1[:, 2, 1] = -np.sin(rotAngle3)
    R1[:, 2, 2] = np.cos(rotAngle3)

    if rotation_sequence == 'ZYX':
        try:
            # Equivalent to C = R1.dot(R2.dot(R3)) for each of N inputs but
            # implemented efficiently in C extension
            C = np.einsum('nij, njk, nkm -> nim', R1, R2, R3)
        except AttributeError:
            # Older NumPy without einsum
            C = np.zeros((N1, 3, 3))
            for i, (R1, R2, R3) in enumerate(zip(R1, R2, R3)):
                C[i] = R1.dot(R2.dot(R3))
    else:
        raise NotImplementedError(
            'Rotation sequences other than ZYX are not currently implemented')

    if (N1 == 1):
        C = C[0]
    if (output_type == 'matrix'):
        C = np.matrix(C)

    return C
Example #12
0
def ecef2ned(ecef,
             lat_ref,
             lon_ref,
             alt_ref,
             latlon_unit='deg',
             alt_unit='m',
             model='wgs84'):
    """
    Transform a vector resolved in ECEF coordinate to its resolution in the NED
    coordinate. The center of the NED coordiante is given by lat_ref, lon_ref,
    and alt_ref.
    
    Parameters
    ----------
    ecef : {(N,3)} input vector expressed in the ECEF frame
    lat_ref : Reference latitude, unit specified by latlon_unit, default in deg
    lon_ref : Reference longitude, unit specified by latlon_unit, default in deg
    alt : Reference altitude, unit specified by alt_unit, default in m
    
    Returns
    -------
    ned : {(N,3)} array like ecef position, unit is the same as alt_unit
    
    Examples
    --------
    >>> import numpy as np
    >>> from navpy import ecef2ned
    >>> lat 
    """
    lat_ref, N1 = _input_check_Nx1(lat_ref)
    lon_ref, N2 = _input_check_Nx1(lon_ref)
    alt_ref, N3 = _input_check_Nx1(alt_ref)

    if ((N1 != 1) or (N2 != 1) or (N3 != 1)):
        raise ValueError('Reference Location can only be 1')

    ecef, N = _input_check_Nx3(ecef)

    ecef = ecef.T

    C = np.zeros((3, 3))

    if (latlon_unit == 'deg'):
        lat_ref = np.deg2rad(lat_ref)
        lon_ref = np.deg2rad(lon_ref)
    elif (latlon_unit == 'rad'):
        pass
    else:
        raise ValueError('Input unit unknown')

    C[0, 0] = -np.sin(lat_ref) * np.cos(lon_ref)
    C[0, 1] = -np.sin(lat_ref) * np.sin(lon_ref)
    C[0, 2] = np.cos(lat_ref)

    C[1, 0] = -np.sin(lon_ref)
    C[1, 1] = np.cos(lon_ref)
    C[1, 2] = 0

    C[2, 0] = -np.cos(lat_ref) * np.cos(lon_ref)
    C[2, 1] = -np.cos(lat_ref) * np.sin(lon_ref)
    C[2, 2] = -np.sin(lat_ref)

    ned = np.dot(C, ecef)
    ned = ned.T

    if (N == 1):
        ned = ned.reshape(3)

    return ned
Example #13
0
def ned2ecef(ned,
             lat_ref,
             lon_ref,
             alt_ref,
             latlon_unit='deg',
             alt_unit='m',
             model='wgs84'):
    """
    Transform a vector resolved in NED (origin given by lat_ref, lon_ref, and alt_ref)
    coordinates to its ECEF representation. 

    Parameters
    ----------
    ned : {(N,3)} input array, units of meters
    lat_ref : Reference latitude, unit specified by latlon_unit, default in deg
    lon_ref : Reference longitude, unit specified by latlon_unit, default in deg
    alt_ref : Reference altitude, unit specified by alt_unit, default in m
    
    Returns
    -------
    ecef : {(N,3)} array like ned vector, in the ECEF frame, units of meters

    Notes
    -----
    The NED vector is treated as a relative vector, and hence the ECEF representation
    returned is NOT converted into an absolute coordinate.  This means that the 
    magnitude of `ned` and `ecef` will be the same (bar numerical differences).
    
    Examples
    --------
    >>> import navpy
    >>> ned = [0, 0, 1]
    >>> lat_ref, lon_ref, alt_ref = 45.0, -93.0, 250.0 # deg, meters
    >>> ecef = navpy.ned2ecef(ned, lat_ref, lon_ref, alt_ref)
    >>> print("NED:", ned)
    >>> print("ECEF:", ecef)
    >>> print("Notice that 'down' is not same as 'ecef-z' coordinate.")
    """
    lat_ref, N1 = _input_check_Nx1(lat_ref)
    lon_ref, N2 = _input_check_Nx1(lon_ref)
    alt_ref, N3 = _input_check_Nx1(alt_ref)

    if ((N1 != 1) or (N2 != 1) or (N3 != 1)):
        raise ValueError('Reference Location can only be 1')

    ned, N = _input_check_Nx3(ned)

    ned = ned.T

    C = np.zeros((3, 3))

    if (latlon_unit == 'deg'):
        lat_ref = np.deg2rad(lat_ref)
        lon_ref = np.deg2rad(lon_ref)
    elif (latlon_unit == 'rad'):
        pass
    else:
        raise ValueError('Input unit unknown')

    C[0, 0] = -np.sin(lat_ref) * np.cos(lon_ref)
    C[0, 1] = -np.sin(lat_ref) * np.sin(lon_ref)
    C[0, 2] = np.cos(lat_ref)

    C[1, 0] = -np.sin(lon_ref)
    C[1, 1] = np.cos(lon_ref)
    C[1, 2] = 0

    C[2, 0] = -np.cos(lat_ref) * np.cos(lon_ref)
    C[2, 1] = -np.cos(lat_ref) * np.sin(lon_ref)
    C[2, 2] = -np.sin(lat_ref)

    # C defines transoformation: ned = C * ecef.  Hence used transpose.
    ecef = np.dot(C.T, ned)
    ecef = ecef.T

    if (N == 1):
        ecef = ecef.reshape(3)

    return ecef