Coordinate Systems
Coordinate system conversions and transformations.
This module provides functions for converting between different coordinate systems commonly used in tracking applications:
Cartesian coordinates (x, y, z)
Spherical coordinates (range, azimuth, elevation)
Polar and cylindrical coordinates
Geodetic coordinates (latitude, longitude, altitude)
Various local tangent plane frames (ENU, NED)
Direction cosine representations (r-u-v)
Rotation representations (matrices, quaternions, Euler angles)
Jacobian matrices for error propagation
- pytcl.coordinate_systems.cart2sphere(cart_points, system_type='standard')[source]
Convert Cartesian coordinates to spherical coordinates.
- Parameters:
cart_points (array_like) – Cartesian coordinates. Can be: - Shape (3,) for a single point [x, y, z] - Shape (3, n) for n points (each column is a point) - Shape (n, 3) will be transposed automatically
system_type ({'standard', 'az-el', 'range-az-el'}, optional) – Spherical coordinate convention: - ‘standard’: Physics convention (r, θ polar from +z, φ azimuth from +x) - ‘az-el’: Tracking convention (r, azimuth from +x, elevation from xy-plane) - ‘range-az-el’: Same as ‘az-el’ (alias) Default is ‘standard’.
- Returns:
r (ndarray) – Range (radial distance from origin).
az (ndarray) – Azimuth angle in radians. - ‘standard’: Angle in xy-plane from +x axis [0, 2π) - ‘az-el’: Angle in xy-plane from +x axis [-π, π]
el (ndarray) – Elevation/polar angle in radians. - ‘standard’: Polar angle from +z axis [0, π] - ‘az-el’: Elevation from xy-plane [-π/2, π/2]
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> x, y, z = 1.0, 1.0, 1.0 >>> r, az, el = cart2sphere([x, y, z], system_type='az-el') >>> r 1.7320508075688772 >>> np.degrees(az) 45.0 >>> np.degrees(el) 35.26438968275465
See also
sphere2cartInverse conversion.
- pytcl.coordinate_systems.sphere2cart(r, az, el, system_type='standard')[source]
Convert spherical coordinates to Cartesian coordinates.
- Parameters:
r (array_like) – Range (radial distance).
az (array_like) – Azimuth angle in radians.
el (array_like) – Elevation/polar angle in radians.
system_type ({'standard', 'az-el', 'range-az-el'}, optional) – Spherical coordinate convention (see cart2sphere).
- Returns:
cart_points – Cartesian coordinates of shape (3,) or (3, n).
- Return type:
ndarray
Examples
>>> r, az, el = 1.732, np.radians(45), np.radians(35.26) >>> cart2sphere(sphere2cart(r, az, el, 'az-el'), 'az-el') (1.732..., 0.785..., 0.615...)
See also
cart2sphereInverse conversion.
- pytcl.coordinate_systems.cart2pol(cart_points)[source]
Convert 2D Cartesian coordinates to polar coordinates.
- Parameters:
cart_points (array_like) – Cartesian coordinates. Can be: - Shape (2,) for a single point [x, y] - Shape (2, n) for n points - Shape (n, 2) will be transposed
- Returns:
r (ndarray) – Radial distance from origin.
theta (ndarray) – Angle in radians from +x axis, in range [-π, π].
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> r, theta = cart2pol([1, 1]) >>> r 1.4142135623730951 >>> np.degrees(theta) 45.0
See also
pol2cartInverse conversion.
- pytcl.coordinate_systems.pol2cart(r, theta)[source]
Convert polar coordinates to 2D Cartesian coordinates.
- Parameters:
r (array_like) – Radial distance.
theta (array_like) – Angle in radians from +x axis.
- Returns:
cart_points – Cartesian coordinates of shape (2,) or (2, n).
- Return type:
ndarray
Examples
>>> x, y = pol2cart(1.414, np.radians(45)) >>> x, y (0.999..., 0.999...)
See also
cart2polInverse conversion.
- pytcl.coordinate_systems.cart2cyl(cart_points)[source]
Convert 3D Cartesian coordinates to cylindrical coordinates.
- Parameters:
cart_points (array_like) – Cartesian coordinates [x, y, z].
- Returns:
rho (ndarray) – Radial distance in xy-plane.
phi (ndarray) – Azimuth angle in radians from +x axis.
z (ndarray) – Height (same as Cartesian z).
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> rho, phi, z = cart2cyl([1, 1, 5]) >>> rho 1.4142135623730951 >>> np.degrees(phi) 45.0 >>> z 5.0
See also
cyl2cartInverse conversion.
- pytcl.coordinate_systems.cyl2cart(rho, phi, z)[source]
Convert cylindrical coordinates to 3D Cartesian coordinates.
- Parameters:
rho (array_like) – Radial distance in xy-plane.
phi (array_like) – Azimuth angle in radians from +x axis.
z (array_like) – Height.
- Returns:
cart_points – Cartesian coordinates of shape (3,) or (3, n).
- Return type:
ndarray
Examples
>>> cart = cyl2cart(1.414, np.radians(45), 5.0) >>> cart array([1.00..., 1.00..., 5. ])
See also
cart2cylInverse conversion.
- pytcl.coordinate_systems.ruv2cart(r, u, v)[source]
Convert r-u-v (range, direction cosines) to Cartesian coordinates.
The r-u-v system uses direction cosines where: - u = cos(az) * cos(el) = x/r - v = sin(az) * cos(el) = y/r - w = sin(el) = z/r (derived from u, v)
- Parameters:
r (array_like) – Range.
u (array_like) – Direction cosine along x-axis.
v (array_like) – Direction cosine along y-axis.
- Returns:
cart_points – Cartesian coordinates.
- Return type:
ndarray
Examples
>>> # Target at 45 deg azimuth, 30 deg elevation, range 100 >>> az, el = np.radians(45), np.radians(30) >>> u = np.cos(az) * np.cos(el) >>> v = np.sin(az) * np.cos(el) >>> cart = ruv2cart(100, u, v) >>> cart array([61.23..., 61.23..., 50. ])
Notes
This representation is common in radar tracking systems.
- pytcl.coordinate_systems.cart2ruv(cart_points)[source]
Convert Cartesian coordinates to r-u-v (range, direction cosines).
- Parameters:
cart_points (array_like) – Cartesian coordinates [x, y, z].
- Returns:
r (ndarray) – Range.
u (ndarray) – Direction cosine along x-axis (x/r).
v (ndarray) – Direction cosine along y-axis (y/r).
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> r, u, v = cart2ruv([100, 0, 0]) >>> r, u, v (100.0, 1.0, 0.0) >>> r, u, v = cart2ruv([50, 50, 50]) >>> r 86.602...
See also
ruv2cartInverse conversion.
- pytcl.coordinate_systems.geodetic2ecef(lat, lon, alt, a=6378137.0, f=0.0033528106647474805)[source]
Convert geodetic coordinates to ECEF (Earth-Centered Earth-Fixed).
- Parameters:
lat (array_like) – Geodetic latitude in radians.
lon (array_like) – Geodetic longitude in radians.
alt (array_like) – Altitude above the reference ellipsoid in meters.
a (float, optional) – Semi-major axis of the reference ellipsoid in meters. Default is WGS84 value.
f (float, optional) – Flattening of the reference ellipsoid. Default is WGS84 value.
- Returns:
ecef – ECEF coordinates [x, y, z] in meters. Shape is (3,) for single point or (3, n) for multiple points.
- Return type:
ndarray
Examples
>>> lat, lon, alt = np.radians(45), np.radians(-75), 100.0 >>> ecef = geodetic2ecef(lat, lon, alt) >>> ecef / 1e6 # In millions of meters array([ 1.14..., -4.29..., 4.48...])
See also
ecef2geodeticInverse conversion.
- pytcl.coordinate_systems.ecef2geodetic(ecef, a=6378137.0, f=0.0033528106647474805, method='iterative')[source]
Convert ECEF coordinates to geodetic coordinates.
- Parameters:
ecef (array_like) – ECEF coordinates [x, y, z] in meters.
a (float, optional) – Semi-major axis of the reference ellipsoid.
f (float, optional) – Flattening of the reference ellipsoid.
method (str, optional) – Algorithm to use: - ‘iterative’: Bowring’s iterative method (default) - ‘direct’: Closed-form solution (Vermeille’s method)
- Returns:
lat (ndarray) – Geodetic latitude in radians.
lon (ndarray) – Geodetic longitude in radians.
alt (ndarray) – Altitude above the ellipsoid in meters.
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> ecef = np.array([1.14e6, -4.29e6, 4.48e6]) >>> lat, lon, alt = ecef2geodetic(ecef) >>> np.degrees(lat), np.degrees(lon) (45.0..., -75.0...)
See also
geodetic2ecefInverse conversion.
- pytcl.coordinate_systems.geodetic2enu(lat, lon, alt, lat_ref, lon_ref, alt_ref, a=6378137.0, f=0.0033528106647474805)[source]
Convert geodetic coordinates to local ENU (East-North-Up) coordinates.
- Parameters:
lat (array_like) – Geodetic latitude in radians.
lon (array_like) – Geodetic longitude in radians.
alt (array_like) – Altitude in meters.
lat_ref (float) – Reference point latitude in radians.
lon_ref (float) – Reference point longitude in radians.
alt_ref (float) – Reference point altitude in meters.
a (float, optional) – Semi-major axis of the reference ellipsoid.
f (float, optional) – Flattening of the reference ellipsoid.
- Returns:
enu – Local ENU coordinates [east, north, up] in meters.
- Return type:
ndarray
Examples
>>> import numpy as np >>> from pytcl.coordinate_systems import geodetic2enu >>> # Reference point: San Francisco Airport (-122.375°, 37.615°) >>> lat_ref = np.radians(37.615) >>> lon_ref = np.radians(-122.375) >>> alt_ref = 0 >>> # Target point: 2 km north, 1 km east of reference >>> # Approximate location using small offsets >>> lat_target = lat_ref + np.radians(0.01) >>> lon_target = lon_ref + np.radians(0.01) >>> alt_target = 100 # 100 m elevation >>> enu = geodetic2enu(lat_target, lon_target, alt_target, ... lat_ref, lon_ref, alt_ref) >>> # ENU coordinates should show positive north and east offsets >>> enu[0] > 0 and enu[1] > 0 # East > 0, North > 0 True >>> enu[2] > 100 # Up should be approximately the altitude difference True
See also
enu2geodeticInverse conversion.
ecef2enuECEF to ENU conversion.
- pytcl.coordinate_systems.ecef2enu(ecef, lat_ref, lon_ref, ecef_ref=None)[source]
Convert ECEF coordinates to local ENU coordinates.
- Parameters:
- Returns:
enu – Local ENU coordinates [east, north, up] in meters.
- Return type:
ndarray
Examples
Convert an aircraft position from ECEF to local ENU frame:
>>> import numpy as np >>> from pytcl.coordinate_systems.conversions import ecef2enu, geodetic2ecef >>> # Reference point (airport at 38.9°N, 77.0°W) >>> lat_ref = np.radians(38.9) >>> lon_ref = np.radians(-77.0) >>> # Aircraft ECEF position >>> ecef_aircraft = np.array([1130000.0, -4830000.0, 3990000.0]) >>> enu = ecef2enu(ecef_aircraft, lat_ref, lon_ref) >>> enu.shape (3,)
- pytcl.coordinate_systems.enu2ecef(enu, lat_ref, lon_ref, ecef_ref=None)[source]
Convert local ENU coordinates to ECEF coordinates.
- Parameters:
- Returns:
ecef – ECEF coordinates [x, y, z] in meters.
- Return type:
ndarray
Examples
Convert local ENU offset to ECEF coordinates:
>>> import numpy as np >>> from pytcl.coordinate_systems.conversions import enu2ecef >>> # Reference point (airport at 38.9°N, 77.0°W) >>> lat_ref = np.radians(38.9) >>> lon_ref = np.radians(-77.0) >>> # Aircraft 1km east, 2km north, 500m up >>> enu = np.array([1000.0, 2000.0, 500.0]) >>> ecef = enu2ecef(enu, lat_ref, lon_ref) >>> ecef.shape (3,)
See also
ecef2enuInverse conversion.
- pytcl.coordinate_systems.ecef2ned(ecef, lat_ref, lon_ref, ecef_ref=None)[source]
Convert ECEF coordinates to local NED (North-East-Down) coordinates.
- Parameters:
- Returns:
ned – Local NED coordinates [north, east, down] in meters.
- Return type:
ndarray
Examples
Convert aircraft ECEF position to local NED frame:
>>> import numpy as np >>> from pytcl.coordinate_systems.conversions import ecef2ned, geodetic2ecef >>> # Reference point (airport) >>> lat_ref = np.radians(38.9) >>> lon_ref = np.radians(-77.0) >>> # Aircraft ECEF position >>> ecef_aircraft = np.array([1130000.0, -4830000.0, 3990000.0]) >>> ned = ecef2ned(ecef_aircraft, lat_ref, lon_ref) >>> ned.shape # [north, east, down] (3,)
- pytcl.coordinate_systems.ned2ecef(ned, lat_ref, lon_ref, ecef_ref=None)[source]
Convert local NED coordinates to ECEF coordinates.
- Parameters:
- Returns:
ecef – ECEF coordinates [x, y, z] in meters.
- Return type:
ndarray
Examples
>>> import numpy as np >>> from pytcl.coordinate_systems import ned2ecef, geodetic2ecef >>> # Reference point: Kennedy Space Center (28.5°N, 80.65°W) >>> lat_ref = np.radians(28.5) >>> lon_ref = np.radians(-80.65) >>> # NED offset from reference: 5 km north, 3 km east, 1 km down >>> ned = np.array([5000.0, 3000.0, 1000.0]) >>> # Convert to ECEF coordinates >>> ecef = ned2ecef(ned, lat_ref, lon_ref) >>> ecef.shape (3,) >>> # Verify roundtrip conversion >>> from pytcl.coordinate_systems import ecef2ned >>> ned_back = ecef2ned(ecef, lat_ref, lon_ref) >>> np.allclose(ned, ned_back, atol=0.1) # Allow small numerical error True
See also
ecef2nedInverse conversion.
- pytcl.coordinate_systems.enu2ned(enu)[source]
Convert ENU coordinates to NED coordinates.
- Parameters:
enu (array_like) – ENU coordinates [east, north, up].
- Returns:
ned – NED coordinates [north, east, down].
- Return type:
ndarray
- pytcl.coordinate_systems.ned2enu(ned)[source]
Convert NED coordinates to ENU coordinates.
- Parameters:
ned (array_like) – NED coordinates [north, east, down].
- Returns:
enu – ENU coordinates [east, north, up].
- Return type:
ndarray
- pytcl.coordinate_systems.geocentric_radius(lat, a=6378137.0, f=0.0033528106647474805)[source]
Compute the geocentric radius at a given geodetic latitude.
- pytcl.coordinate_systems.prime_vertical_radius(lat, a=6378137.0, f=0.0033528106647474805)[source]
Compute the prime vertical radius of curvature.
- pytcl.coordinate_systems.meridional_radius(lat, a=6378137.0, f=0.0033528106647474805)[source]
Compute the meridional radius of curvature.
- pytcl.coordinate_systems.rotx(angle)[source]
Create rotation matrix for rotation about the x-axis.
- Parameters:
angle (float) – Rotation angle in radians.
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> R = rotx(np.pi/2) # 90 degree rotation about x >>> R @ [0, 1, 0] # y-axis maps to z-axis array([0., 0., 1.])
- pytcl.coordinate_systems.roty(angle)[source]
Create rotation matrix for rotation about the y-axis.
- Parameters:
angle (float) – Rotation angle in radians.
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> R = roty(np.pi/2) # 90 degree rotation about y >>> R @ [1, 0, 0] # x-axis maps to -z-axis array([ 0., 0., -1.])
- pytcl.coordinate_systems.rotz(angle)[source]
Create rotation matrix for rotation about the z-axis.
- Parameters:
angle (float) – Rotation angle in radians.
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> R = rotz(np.pi/2) # 90 degree rotation about z >>> R @ [1, 0, 0] # x-axis maps to y-axis array([0., 1., 0.])
- pytcl.coordinate_systems.euler2rotmat(angles, sequence='ZYX')[source]
Convert Euler angles to rotation matrix.
- Parameters:
angles (array_like) – Three Euler angles in radians [angle1, angle2, angle3].
sequence (str, optional) – Rotation sequence (e.g., ‘ZYX’, ‘XYZ’, ‘ZXZ’). Default is ‘ZYX’ (aerospace convention: yaw-pitch-roll).
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> yaw, pitch, roll = np.radians([45, 30, 15]) >>> R = euler2rotmat([yaw, pitch, roll], 'ZYX')
Notes
The rotation is applied in the order specified by the sequence, from right to left. For ‘ZYX’: R = Rz(yaw) @ Ry(pitch) @ Rx(roll).
- pytcl.coordinate_systems.rotmat2euler(R, sequence='ZYX')[source]
Convert rotation matrix to Euler angles.
- Parameters:
R (array_like) – 3x3 rotation matrix.
sequence (str, optional) – Rotation sequence. Default is ‘ZYX’.
- Returns:
angles – Three Euler angles in radians.
- Return type:
ndarray
Examples
>>> R = rotz(np.radians(45)) @ roty(np.radians(30)) @ rotx(np.radians(15)) >>> angles = rotmat2euler(R, 'ZYX') >>> np.degrees(angles) array([45., 30., 15.])
Notes
May have singularities (gimbal lock) at certain angles.
- pytcl.coordinate_systems.axisangle2rotmat(axis, angle)[source]
Convert axis-angle representation to rotation matrix.
- Parameters:
axis (array_like) – Unit vector defining the rotation axis [ax, ay, az].
angle (float) – Rotation angle in radians.
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> axis = [0, 0, 1] # Z-axis >>> R = axisangle2rotmat(axis, np.pi/2) # 90 deg about Z >>> R @ [1, 0, 0] # x-axis maps to y-axis array([0., 1., 0.])
Notes
Uses Rodrigues’ rotation formula.
- pytcl.coordinate_systems.rotmat2axisangle(R)[source]
Convert rotation matrix to axis-angle representation.
- Parameters:
R (array_like) – 3x3 rotation matrix.
- Returns:
axis (ndarray) – Unit vector rotation axis.
angle (float) – Rotation angle in radians [0, π].
- Return type:
Examples
>>> R = rotz(np.pi/2) # 90 deg about Z >>> axis, angle = rotmat2axisangle(R) >>> axis # Z-axis array([0., 0., 1.]) >>> np.degrees(angle) 90.0
- pytcl.coordinate_systems.quat2rotmat(q)[source]
Convert quaternion to rotation matrix.
- Parameters:
q (array_like) – Quaternion [qw, qx, qy, qz] (scalar-first convention).
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> q = [1, 0, 0, 0] # Identity quaternion >>> quat2rotmat(q) array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
- pytcl.coordinate_systems.rotmat2quat(R)[source]
Convert rotation matrix to quaternion.
- Parameters:
R (array_like) – 3x3 rotation matrix.
- Returns:
q – Quaternion [qw, qx, qy, qz] (scalar-first, positive qw).
- Return type:
ndarray
Examples
>>> R = np.eye(3) # Identity rotation >>> rotmat2quat(R) array([1., 0., 0., 0.])
Notes
Uses Shepperd’s method for numerical stability.
- pytcl.coordinate_systems.euler2quat(angles, sequence='ZYX')[source]
Convert Euler angles to quaternion.
- Parameters:
angles (array_like) – Three Euler angles in radians.
sequence (str, optional) – Rotation sequence. Default is ‘ZYX’.
- Returns:
q – Quaternion [qw, qx, qy, qz].
- Return type:
ndarray
Examples
Convert yaw-pitch-roll angles to quaternion:
>>> import numpy as np >>> from pytcl.coordinate_systems.rotations import euler2quat >>> # 45° yaw, 30° pitch, 0° roll >>> angles = np.radians([45, 30, 0]) >>> q = euler2quat(angles, sequence='ZYX') >>> q.shape (4,) >>> np.abs(q[0]) > 0.5 # scalar part should be significant True
See also
quat2eulerInverse conversion.
euler2rotmatConvert to rotation matrix instead.
- pytcl.coordinate_systems.quat2euler(q, sequence='ZYX')[source]
Convert quaternion to Euler angles.
- Parameters:
q (array_like) – Quaternion [qw, qx, qy, qz].
sequence (str, optional) – Rotation sequence. Default is ‘ZYX’.
- Returns:
angles – Three Euler angles in radians.
- Return type:
ndarray
Examples
>>> q = [1, 0, 0, 0] # Identity quaternion >>> angles = quat2euler(q, 'ZYX') >>> np.allclose(angles, [0, 0, 0]) True
- pytcl.coordinate_systems.quat_multiply(q1, q2)[source]
Multiply two quaternions.
- Parameters:
q1 (array_like) – First quaternion [qw, qx, qy, qz].
q2 (array_like) – Second quaternion.
- Returns:
q – Product quaternion q1 * q2.
- Return type:
ndarray
Notes
Quaternion multiplication represents composition of rotations. q1 * q2 applies q2 first, then q1.
Examples
Combine two rotations using quaternion multiplication:
>>> import numpy as np >>> from pytcl.coordinate_systems.rotations import quat_multiply, euler2quat >>> # 90° rotation about Z, then 45° about X >>> q_z90 = euler2quat(np.radians([90, 0, 0]), 'ZYX') >>> q_x45 = euler2quat(np.radians([0, 0, 45]), 'ZYX') >>> q_combined = quat_multiply(q_z90, q_x45) >>> q_combined.shape (4,) >>> np.isclose(np.linalg.norm(q_combined), 1.0) # unit quaternion True
See also
quat_inverseCompute quaternion inverse.
quat_rotateRotate a vector by a quaternion.
- pytcl.coordinate_systems.quat_conjugate(q)[source]
Compute quaternion conjugate.
- Parameters:
q (array_like) – Quaternion [qw, qx, qy, qz].
- Returns:
q_conj – Conjugate quaternion [qw, -qx, -qy, -qz].
- Return type:
ndarray
Examples
>>> quat_conjugate([0.707, 0.707, 0, 0]) array([ 0.707, -0.707, -0. , -0. ])
- pytcl.coordinate_systems.quat_inverse(q)[source]
Compute quaternion inverse.
- Parameters:
q (array_like) – Quaternion [qw, qx, qy, qz].
- Returns:
q_inv – Inverse quaternion.
- Return type:
ndarray
Examples
>>> q = euler2quat(np.radians([45, 0, 0]), 'ZYX') >>> q_inv = quat_inverse(q) >>> quat_multiply(q, q_inv) # Should be identity array([1., 0., 0., 0.])
Notes
For unit quaternions, inverse equals conjugate.
- pytcl.coordinate_systems.quat_rotate(q, v)[source]
Rotate a vector using a quaternion.
- Parameters:
q (array_like) – Quaternion [qw, qx, qy, qz].
v (array_like) – Vector to rotate [x, y, z].
- Returns:
v_rot – Rotated vector.
- Return type:
ndarray
Examples
>>> # 90 degree rotation about z-axis >>> q = euler2quat(np.radians([90, 0, 0]), 'ZYX') >>> v = np.array([1.0, 0.0, 0.0]) >>> v_rot = quat_rotate(q, v) >>> v_rot # x-axis becomes y-axis array([0., 1., 0.])
Notes
Computes q * v * q^(-1) where v is treated as a pure quaternion.
- pytcl.coordinate_systems.slerp(q1, q2, t)[source]
Spherical linear interpolation between two quaternions.
- Parameters:
q1 (array_like) – Start quaternion.
q2 (array_like) – End quaternion.
t (float) – Interpolation parameter in [0, 1].
- Returns:
q – Interpolated quaternion.
- Return type:
ndarray
Examples
>>> q1 = np.array([1, 0, 0, 0]) # identity >>> q2 = euler2quat(np.radians([90, 0, 0]), 'ZYX') # 90 deg about z >>> q_mid = slerp(q1, q2, 0.5) # halfway = 45 deg >>> angles = quat2euler(q_mid, 'ZYX') >>> np.degrees(angles[0]) # yaw should be ~45 45.0...
- pytcl.coordinate_systems.rodrigues2rotmat(rvec)[source]
Convert Rodrigues vector to rotation matrix.
- Parameters:
rvec (array_like) – Rodrigues vector (axis * angle).
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> rvec = [0, 0, np.pi/2] # 90 deg about Z >>> R = rodrigues2rotmat(rvec) >>> R @ [1, 0, 0] # x-axis maps to y-axis array([0., 1., 0.])
Notes
The Rodrigues vector encodes both the rotation axis and angle: rvec = axis * angle, where |rvec| = angle.
- pytcl.coordinate_systems.rotmat2rodrigues(R)[source]
Convert rotation matrix to Rodrigues vector.
- Parameters:
R (array_like) – 3x3 rotation matrix.
- Returns:
rvec – Rodrigues vector (axis * angle).
- Return type:
ndarray
Examples
>>> R = rotz(np.pi/2) # 90 deg about Z >>> rvec = rotmat2rodrigues(R) >>> np.linalg.norm(rvec) # magnitude is the angle 1.5707...
- pytcl.coordinate_systems.dcm_rate(R, omega)[source]
Compute the time derivative of a rotation matrix.
- Parameters:
R (array_like) – Current rotation matrix.
omega (array_like) – Angular velocity vector [wx, wy, wz] in body frame.
- Returns:
R_dot – Time derivative of R.
- Return type:
ndarray
Examples
>>> R = np.eye(3) >>> omega = [0, 0, 1] # 1 rad/s about Z >>> R_dot = dcm_rate(R, omega) >>> R_dot[0, 1] # Off-diagonal elements show rotation -1.0
Notes
R_dot = R @ skew(omega)
- pytcl.coordinate_systems.is_rotation_matrix(R, tol=1e-06)[source]
Check if a matrix is a valid rotation matrix.
- Parameters:
R (array_like) – Matrix to check.
tol (float, optional) – Tolerance for numerical checks.
- Returns:
valid – True if R is a valid rotation matrix.
- Return type:
Examples
>>> R = rotx(np.pi/4) >>> is_rotation_matrix(R) True >>> is_rotation_matrix(np.eye(3) * 2) # not orthonormal False
- pytcl.coordinate_systems.mercator(lat, lon, lon0=0.0, a=6378137.0, e=np.float64(0.08181919084262149))[source]
Ellipsoidal Mercator projection (forward).
The Mercator projection is a conformal cylindrical projection where rhumb lines (lines of constant bearing) appear as straight lines. Scale increases with latitude, becoming infinite at the poles.
- Parameters:
- Returns:
Projected coordinates with scale and convergence.
- Return type:
ProjectionResult
Notes
The Mercator projection is conformal, meaning it preserves local angles. However, it distorts areas, especially at high latitudes.
Examples
>>> import numpy as np >>> result = mercator(np.radians(45), np.radians(-75)) >>> print(f"x={result.x:.1f}, y={result.y:.1f}")
- pytcl.coordinate_systems.mercator_inverse(x, y, lon0=0.0, a=6378137.0, e=np.float64(0.08181919084262149), tol=1e-12, max_iter=10)[source]
Ellipsoidal Mercator projection (inverse).
- Parameters:
x (float) – Easting coordinate in meters.
y (float) – Northing coordinate in meters.
lon0 (float, optional) – Central meridian in radians. Default is 0.
a (float, optional) – Semi-major axis in meters. Default is WGS84.
e (float, optional) – First eccentricity. Default is WGS84.
tol (float, optional) – Convergence tolerance. Default is 1e-12.
max_iter (int, optional) – Maximum iterations. Default is 10.
- Returns:
(latitude, longitude) in radians.
- Return type:
Examples
>>> import numpy as np >>> lat, lon = mercator_inverse(1000000, 5000000)
- pytcl.coordinate_systems.transverse_mercator(lat, lon, lat0=0.0, lon0=0.0, k0=1.0, a=6378137.0, e2=np.float64(0.006694379990141316))[source]
Transverse Mercator projection (forward).
The Transverse Mercator is a conformal projection that rotates the cylinder to be tangent along a meridian. It’s the basis for UTM.
- Parameters:
lat (float) – Geodetic latitude in radians.
lon (float) – Longitude in radians.
lat0 (float, optional) – Origin latitude in radians. Default is 0.
lon0 (float, optional) – Central meridian in radians. Default is 0.
k0 (float, optional) – Scale factor at central meridian. Default is 1.0.
a (float, optional) – Semi-major axis in meters. Default is WGS84.
e2 (float, optional) – First eccentricity squared. Default is WGS84.
- Returns:
Projected coordinates with scale and convergence.
- Return type:
ProjectionResult
Notes
This implementation uses the Redfearn series expansion, accurate to about 1 meter within 4 degrees of the central meridian.
Examples
>>> import numpy as np >>> result = transverse_mercator(np.radians(45), np.radians(-75), ... lon0=np.radians(-75))
- pytcl.coordinate_systems.transverse_mercator_inverse(x, y, lat0=0.0, lon0=0.0, k0=1.0, a=6378137.0, e2=np.float64(0.006694379990141316))[source]
Transverse Mercator projection (inverse).
- Parameters:
x (float) – Easting coordinate in meters.
y (float) – Northing coordinate in meters.
lat0 (float, optional) – Origin latitude in radians. Default is 0.
lon0 (float, optional) – Central meridian in radians. Default is 0.
k0 (float, optional) – Scale factor at central meridian. Default is 1.0.
a (float, optional) – Semi-major axis in meters. Default is WGS84.
e2 (float, optional) – First eccentricity squared. Default is WGS84.
- Returns:
(latitude, longitude) in radians.
- Return type:
- pytcl.coordinate_systems.utm_zone(lon, lat=0.0)[source]
Determine UTM zone number from longitude.
- Parameters:
- Returns:
UTM zone number (1-60).
- Return type:
Notes
Standard zones are 6 degrees wide. Special zones exist for Norway and Svalbard.
- pytcl.coordinate_systems.geodetic2utm(lat, lon, zone=None)[source]
Convert geodetic coordinates to UTM.
- Parameters:
- Returns:
UTM coordinates with zone information.
- Return type:
UTMResult
Examples
>>> import numpy as np >>> result = geodetic2utm(np.radians(45.0), np.radians(-75.5)) >>> print(f"Zone {result.zone}{result.hemisphere}: " ... f"E={result.easting:.1f}, N={result.northing:.1f}")
- pytcl.coordinate_systems.utm2geodetic(easting, northing, zone, hemisphere='N')[source]
Convert UTM coordinates to geodetic.
- Parameters:
- Returns:
(latitude, longitude) in radians.
- Return type:
Examples
>>> lat, lon = utm2geodetic(500000, 5000000, 18, 'N') >>> print(f"Lat: {np.degrees(lat):.4f}, Lon: {np.degrees(lon):.4f}")
- pytcl.coordinate_systems.stereographic(lat, lon, lat0, lon0, k0=1.0, a=6378137.0, e2=np.float64(0.006694379990141316))[source]
Oblique stereographic projection (forward).
The stereographic projection is conformal and azimuthal. It’s commonly used for polar regions and local surveys.
- Parameters:
lat (float) – Geodetic latitude in radians.
lon (float) – Longitude in radians.
lat0 (float) – Center latitude in radians.
lon0 (float) – Center longitude in radians.
k0 (float, optional) – Scale factor at center. Default is 1.0.
a (float, optional) – Semi-major axis in meters. Default is WGS84.
e2 (float, optional) – First eccentricity squared. Default is WGS84.
- Returns:
Projected coordinates with scale and convergence.
- Return type:
ProjectionResult
Notes
For polar stereographic, use lat0 = +-pi/2.
Examples
>>> import numpy as np >>> # Polar stereographic centered at North Pole >>> result = stereographic(np.radians(85), np.radians(45), ... np.radians(90), 0)
- pytcl.coordinate_systems.stereographic_inverse(x, y, lat0, lon0, k0=1.0, a=6378137.0, e2=np.float64(0.006694379990141316), tol=1e-12, max_iter=10)[source]
Oblique stereographic projection (inverse).
- Parameters:
x (float) – Easting in meters.
y (float) – Northing in meters.
lat0 (float) – Center latitude in radians.
lon0 (float) – Center longitude in radians.
k0 (float, optional) – Scale factor at center. Default is 1.0.
a (float, optional) – Semi-major axis in meters. Default is WGS84.
e2 (float, optional) – First eccentricity squared. Default is WGS84.
tol (float, optional) – Convergence tolerance. Default is 1e-12.
max_iter (int, optional) – Maximum iterations. Default is 10.
- Returns:
(latitude, longitude) in radians.
- Return type:
- pytcl.coordinate_systems.polar_stereographic(lat, lon, north=True, k0=0.994, a=6378137.0, e2=np.float64(0.006694379990141316))[source]
Polar stereographic projection (forward).
Standard polar stereographic used for polar regions.
- Parameters:
lat (float) – Geodetic latitude in radians.
lon (float) – Longitude in radians.
north (bool, optional) – True for North Pole center, False for South Pole. Default is True.
k0 (float, optional) – Scale factor at pole. Default is 0.994 (UPS standard).
a (float, optional) – Semi-major axis in meters. Default is WGS84.
e2 (float, optional) – First eccentricity squared. Default is WGS84.
- Returns:
Projected coordinates with scale and convergence.
- Return type:
ProjectionResult
Examples
>>> import numpy as np >>> # Arctic location >>> result = polar_stereographic(np.radians(80), np.radians(45))
- pytcl.coordinate_systems.lambert_conformal_conic(lat, lon, lat0, lon0, lat1, lat2, k0=1.0, a=6378137.0, e2=np.float64(0.006694379990141316))[source]
Lambert Conformal Conic projection (forward).
A conformal conic projection with two standard parallels where scale is exact. Good for mid-latitude regions with east-west extent.
- Parameters:
lat (float) – Geodetic latitude in radians.
lon (float) – Longitude in radians.
lat0 (float) – Origin latitude in radians.
lon0 (float) – Central meridian in radians.
lat1 (float) – First standard parallel in radians.
lat2 (float) – Second standard parallel in radians.
k0 (float, optional) – Scale factor. Default is 1.0.
a (float, optional) – Semi-major axis in meters. Default is WGS84.
e2 (float, optional) – First eccentricity squared. Default is WGS84.
- Returns:
Projected coordinates with scale and convergence.
- Return type:
ProjectionResult
Examples
>>> import numpy as np >>> # Continental US projection >>> result = lambert_conformal_conic( ... np.radians(40), np.radians(-100), ... lat0=np.radians(39), lon0=np.radians(-96), ... lat1=np.radians(33), lat2=np.radians(45) ... )
- pytcl.coordinate_systems.lambert_conformal_conic_inverse(x, y, lat0, lon0, lat1, lat2, k0=1.0, a=6378137.0, e2=np.float64(0.006694379990141316), tol=1e-12, max_iter=10)[source]
Lambert Conformal Conic projection (inverse).
- Parameters:
x (float) – Easting in meters.
y (float) – Northing in meters.
lat0 (float) – Origin latitude in radians.
lon0 (float) – Central meridian in radians.
lat1 (float) – First standard parallel in radians.
lat2 (float) – Second standard parallel in radians.
k0 (float, optional) – Scale factor. Default is 1.0.
a (float, optional) – Semi-major axis in meters. Default is WGS84.
e2 (float, optional) – First eccentricity squared. Default is WGS84.
tol (float, optional) – Convergence tolerance. Default is 1e-12.
max_iter (int, optional) – Maximum iterations. Default is 10.
- Returns:
(latitude, longitude) in radians.
- Return type:
- pytcl.coordinate_systems.azimuthal_equidistant(lat, lon, lat0, lon0, a=6378137.0, e2=np.float64(0.006694379990141316))[source]
Azimuthal equidistant projection (forward).
Distances from the center point are preserved. Useful for showing distances from a specific location (e.g., radio coverage).
- Parameters:
lat (float) – Geodetic latitude in radians.
lon (float) – Longitude in radians.
lat0 (float) – Center latitude in radians.
lon0 (float) – Center longitude in radians.
a (float, optional) – Semi-major axis in meters. Default is WGS84.
e2 (float, optional) – First eccentricity squared. Default is WGS84.
- Returns:
Projected coordinates with scale and convergence.
- Return type:
ProjectionResult
Notes
This implementation uses the spherical approximation for simplicity. For high accuracy over long distances, geodesic calculations should be used.
Examples
>>> import numpy as np >>> result = azimuthal_equidistant(np.radians(40), np.radians(-75), ... np.radians(38.9), np.radians(-77))
- pytcl.coordinate_systems.azimuthal_equidistant_inverse(x, y, lat0, lon0, a=6378137.0, e2=np.float64(0.006694379990141316))[source]
Azimuthal equidistant projection (inverse).
- Parameters:
- Returns:
(latitude, longitude) in radians.
- Return type:
- pytcl.coordinate_systems.spherical_jacobian(cart_point, system_type='az-el')[source]
Compute Jacobian of Cartesian to spherical transformation.
Returns the Jacobian matrix J where d[r, az, el] = J @ d[x, y, z].
- Parameters:
cart_point (array_like) – Cartesian coordinates [x, y, z].
system_type ({'standard', 'az-el'}, optional) – Spherical coordinate convention. Default is ‘az-el’.
- Returns:
J – 3x3 Jacobian matrix.
- Return type:
ndarray
Notes
For tracking convention (‘az-el’): - r = sqrt(x² + y² + z²) - az = atan2(y, x) - el = atan2(z, sqrt(x² + y²))
Examples
>>> J = spherical_jacobian([1, 1, 1]) >>> J.shape (3, 3)
- pytcl.coordinate_systems.spherical_jacobian_inv(r, az, el, system_type='az-el')[source]
Compute Jacobian of spherical to Cartesian transformation.
Returns the Jacobian matrix J where d[x, y, z] = J @ d[r, az, el].
- pytcl.coordinate_systems.polar_jacobian(cart_point)[source]
Compute Jacobian of 2D Cartesian to polar transformation.
Returns J where d[r, theta] = J @ d[x, y].
- Parameters:
cart_point (array_like) – Cartesian coordinates [x, y].
- Returns:
J – 2x2 Jacobian matrix.
- Return type:
ndarray
- pytcl.coordinate_systems.polar_jacobian_inv(r, theta)[source]
Compute Jacobian of polar to 2D Cartesian transformation.
Returns J where d[x, y] = J @ d[r, theta].
- pytcl.coordinate_systems.ruv_jacobian(cart_point)[source]
Compute Jacobian of Cartesian to r-u-v transformation.
The r-u-v representation uses range and direction cosines: - u = x/r (direction cosine along x) - v = y/r (direction cosine along y)
- Parameters:
cart_point (array_like) – Cartesian coordinates [x, y, z].
- Returns:
J – 3x3 Jacobian matrix where d[r, u, v] = J @ d[x, y, z].
- Return type:
ndarray
- pytcl.coordinate_systems.enu_jacobian(lat, lon)[source]
Compute Jacobian of ECEF to ENU transformation.
Returns J where d[e, n, u] = J @ d[x, y, z].
- Parameters:
- Returns:
J – 3x3 rotation matrix (Jacobian is constant for this linear transformation).
- Return type:
ndarray
Notes
Uses cached computation with quantized inputs for performance.
- pytcl.coordinate_systems.ned_jacobian(lat, lon)[source]
Compute Jacobian of ECEF to NED transformation.
- Parameters:
- Returns:
J – 3x3 rotation matrix.
- Return type:
ndarray
Notes
Uses cached computation with quantized inputs for performance.
- pytcl.coordinate_systems.geodetic_jacobian(lat, lon, alt, a=6378137.0, f=0.0033528106647474805)[source]
Compute Jacobian of geodetic to ECEF transformation.
Returns J where d[x, y, z] = J @ d[lat, lon, alt].
- Parameters:
- Returns:
J – 3x3 Jacobian matrix.
- Return type:
ndarray
- pytcl.coordinate_systems.cross_covariance_transform(J, P)[source]
Transform a covariance matrix through a Jacobian.
Computes P_new = J @ P @ J.T for error propagation.
- Parameters:
J (array_like) – Jacobian matrix of the transformation.
P (array_like) – Original covariance matrix.
- Returns:
P_new – Transformed covariance matrix.
- Return type:
ndarray
Examples
>>> # Transform spherical covariance to Cartesian >>> P_sph = np.diag([1, 0.01, 0.01]) # [r, az, el] variances >>> r, az, el = 1000, np.radians(45), np.radians(30) >>> J = spherical_jacobian_inv(r, az, el) >>> P_cart = cross_covariance_transform(J, P_sph)
- pytcl.coordinate_systems.numerical_jacobian(func, x, dx=1e-07)[source]
Compute Jacobian numerically using central differences.
- Parameters:
func (callable) – Function f(x) -> y.
x (array_like) – Point at which to compute Jacobian.
dx (float, optional) – Step size for finite differences.
- Returns:
J – Jacobian matrix.
- Return type:
ndarray
Conversions
Coordinate conversions.
This module provides: - Spherical/polar coordinate conversions - Geodetic (lat/lon/alt) to ECEF conversions - Local tangent plane frames (ENU, NED) - Direction cosine representations (r-u-v)
- pytcl.coordinate_systems.conversions.cart2sphere(cart_points, system_type='standard')[source]
Convert Cartesian coordinates to spherical coordinates.
- Parameters:
cart_points (array_like) – Cartesian coordinates. Can be: - Shape (3,) for a single point [x, y, z] - Shape (3, n) for n points (each column is a point) - Shape (n, 3) will be transposed automatically
system_type ({'standard', 'az-el', 'range-az-el'}, optional) – Spherical coordinate convention: - ‘standard’: Physics convention (r, θ polar from +z, φ azimuth from +x) - ‘az-el’: Tracking convention (r, azimuth from +x, elevation from xy-plane) - ‘range-az-el’: Same as ‘az-el’ (alias) Default is ‘standard’.
- Returns:
r (ndarray) – Range (radial distance from origin).
az (ndarray) – Azimuth angle in radians. - ‘standard’: Angle in xy-plane from +x axis [0, 2π) - ‘az-el’: Angle in xy-plane from +x axis [-π, π]
el (ndarray) – Elevation/polar angle in radians. - ‘standard’: Polar angle from +z axis [0, π] - ‘az-el’: Elevation from xy-plane [-π/2, π/2]
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> x, y, z = 1.0, 1.0, 1.0 >>> r, az, el = cart2sphere([x, y, z], system_type='az-el') >>> r 1.7320508075688772 >>> np.degrees(az) 45.0 >>> np.degrees(el) 35.26438968275465
See also
sphere2cartInverse conversion.
- pytcl.coordinate_systems.conversions.sphere2cart(r, az, el, system_type='standard')[source]
Convert spherical coordinates to Cartesian coordinates.
- Parameters:
r (array_like) – Range (radial distance).
az (array_like) – Azimuth angle in radians.
el (array_like) – Elevation/polar angle in radians.
system_type ({'standard', 'az-el', 'range-az-el'}, optional) – Spherical coordinate convention (see cart2sphere).
- Returns:
cart_points – Cartesian coordinates of shape (3,) or (3, n).
- Return type:
ndarray
Examples
>>> r, az, el = 1.732, np.radians(45), np.radians(35.26) >>> cart2sphere(sphere2cart(r, az, el, 'az-el'), 'az-el') (1.732..., 0.785..., 0.615...)
See also
cart2sphereInverse conversion.
- pytcl.coordinate_systems.conversions.cart2pol(cart_points)[source]
Convert 2D Cartesian coordinates to polar coordinates.
- Parameters:
cart_points (array_like) – Cartesian coordinates. Can be: - Shape (2,) for a single point [x, y] - Shape (2, n) for n points - Shape (n, 2) will be transposed
- Returns:
r (ndarray) – Radial distance from origin.
theta (ndarray) – Angle in radians from +x axis, in range [-π, π].
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> r, theta = cart2pol([1, 1]) >>> r 1.4142135623730951 >>> np.degrees(theta) 45.0
See also
pol2cartInverse conversion.
- pytcl.coordinate_systems.conversions.pol2cart(r, theta)[source]
Convert polar coordinates to 2D Cartesian coordinates.
- Parameters:
r (array_like) – Radial distance.
theta (array_like) – Angle in radians from +x axis.
- Returns:
cart_points – Cartesian coordinates of shape (2,) or (2, n).
- Return type:
ndarray
Examples
>>> x, y = pol2cart(1.414, np.radians(45)) >>> x, y (0.999..., 0.999...)
See also
cart2polInverse conversion.
- pytcl.coordinate_systems.conversions.cart2cyl(cart_points)[source]
Convert 3D Cartesian coordinates to cylindrical coordinates.
- Parameters:
cart_points (array_like) – Cartesian coordinates [x, y, z].
- Returns:
rho (ndarray) – Radial distance in xy-plane.
phi (ndarray) – Azimuth angle in radians from +x axis.
z (ndarray) – Height (same as Cartesian z).
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> rho, phi, z = cart2cyl([1, 1, 5]) >>> rho 1.4142135623730951 >>> np.degrees(phi) 45.0 >>> z 5.0
See also
cyl2cartInverse conversion.
- pytcl.coordinate_systems.conversions.cyl2cart(rho, phi, z)[source]
Convert cylindrical coordinates to 3D Cartesian coordinates.
- Parameters:
rho (array_like) – Radial distance in xy-plane.
phi (array_like) – Azimuth angle in radians from +x axis.
z (array_like) – Height.
- Returns:
cart_points – Cartesian coordinates of shape (3,) or (3, n).
- Return type:
ndarray
Examples
>>> cart = cyl2cart(1.414, np.radians(45), 5.0) >>> cart array([1.00..., 1.00..., 5. ])
See also
cart2cylInverse conversion.
- pytcl.coordinate_systems.conversions.ruv2cart(r, u, v)[source]
Convert r-u-v (range, direction cosines) to Cartesian coordinates.
The r-u-v system uses direction cosines where: - u = cos(az) * cos(el) = x/r - v = sin(az) * cos(el) = y/r - w = sin(el) = z/r (derived from u, v)
- Parameters:
r (array_like) – Range.
u (array_like) – Direction cosine along x-axis.
v (array_like) – Direction cosine along y-axis.
- Returns:
cart_points – Cartesian coordinates.
- Return type:
ndarray
Examples
>>> # Target at 45 deg azimuth, 30 deg elevation, range 100 >>> az, el = np.radians(45), np.radians(30) >>> u = np.cos(az) * np.cos(el) >>> v = np.sin(az) * np.cos(el) >>> cart = ruv2cart(100, u, v) >>> cart array([61.23..., 61.23..., 50. ])
Notes
This representation is common in radar tracking systems.
- pytcl.coordinate_systems.conversions.cart2ruv(cart_points)[source]
Convert Cartesian coordinates to r-u-v (range, direction cosines).
- Parameters:
cart_points (array_like) – Cartesian coordinates [x, y, z].
- Returns:
r (ndarray) – Range.
u (ndarray) – Direction cosine along x-axis (x/r).
v (ndarray) – Direction cosine along y-axis (y/r).
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> r, u, v = cart2ruv([100, 0, 0]) >>> r, u, v (100.0, 1.0, 0.0) >>> r, u, v = cart2ruv([50, 50, 50]) >>> r 86.602...
See also
ruv2cartInverse conversion.
- pytcl.coordinate_systems.conversions.geodetic2ecef(lat, lon, alt, a=6378137.0, f=0.0033528106647474805)[source]
Convert geodetic coordinates to ECEF (Earth-Centered Earth-Fixed).
- Parameters:
lat (array_like) – Geodetic latitude in radians.
lon (array_like) – Geodetic longitude in radians.
alt (array_like) – Altitude above the reference ellipsoid in meters.
a (float, optional) – Semi-major axis of the reference ellipsoid in meters. Default is WGS84 value.
f (float, optional) – Flattening of the reference ellipsoid. Default is WGS84 value.
- Returns:
ecef – ECEF coordinates [x, y, z] in meters. Shape is (3,) for single point or (3, n) for multiple points.
- Return type:
ndarray
Examples
>>> lat, lon, alt = np.radians(45), np.radians(-75), 100.0 >>> ecef = geodetic2ecef(lat, lon, alt) >>> ecef / 1e6 # In millions of meters array([ 1.14..., -4.29..., 4.48...])
See also
ecef2geodeticInverse conversion.
- pytcl.coordinate_systems.conversions.ecef2geodetic(ecef, a=6378137.0, f=0.0033528106647474805, method='iterative')[source]
Convert ECEF coordinates to geodetic coordinates.
- Parameters:
ecef (array_like) – ECEF coordinates [x, y, z] in meters.
a (float, optional) – Semi-major axis of the reference ellipsoid.
f (float, optional) – Flattening of the reference ellipsoid.
method (str, optional) – Algorithm to use: - ‘iterative’: Bowring’s iterative method (default) - ‘direct’: Closed-form solution (Vermeille’s method)
- Returns:
lat (ndarray) – Geodetic latitude in radians.
lon (ndarray) – Geodetic longitude in radians.
alt (ndarray) – Altitude above the ellipsoid in meters.
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> ecef = np.array([1.14e6, -4.29e6, 4.48e6]) >>> lat, lon, alt = ecef2geodetic(ecef) >>> np.degrees(lat), np.degrees(lon) (45.0..., -75.0...)
See also
geodetic2ecefInverse conversion.
- pytcl.coordinate_systems.conversions.geodetic2enu(lat, lon, alt, lat_ref, lon_ref, alt_ref, a=6378137.0, f=0.0033528106647474805)[source]
Convert geodetic coordinates to local ENU (East-North-Up) coordinates.
- Parameters:
lat (array_like) – Geodetic latitude in radians.
lon (array_like) – Geodetic longitude in radians.
alt (array_like) – Altitude in meters.
lat_ref (float) – Reference point latitude in radians.
lon_ref (float) – Reference point longitude in radians.
alt_ref (float) – Reference point altitude in meters.
a (float, optional) – Semi-major axis of the reference ellipsoid.
f (float, optional) – Flattening of the reference ellipsoid.
- Returns:
enu – Local ENU coordinates [east, north, up] in meters.
- Return type:
ndarray
Examples
>>> import numpy as np >>> from pytcl.coordinate_systems import geodetic2enu >>> # Reference point: San Francisco Airport (-122.375°, 37.615°) >>> lat_ref = np.radians(37.615) >>> lon_ref = np.radians(-122.375) >>> alt_ref = 0 >>> # Target point: 2 km north, 1 km east of reference >>> # Approximate location using small offsets >>> lat_target = lat_ref + np.radians(0.01) >>> lon_target = lon_ref + np.radians(0.01) >>> alt_target = 100 # 100 m elevation >>> enu = geodetic2enu(lat_target, lon_target, alt_target, ... lat_ref, lon_ref, alt_ref) >>> # ENU coordinates should show positive north and east offsets >>> enu[0] > 0 and enu[1] > 0 # East > 0, North > 0 True >>> enu[2] > 100 # Up should be approximately the altitude difference True
See also
enu2geodeticInverse conversion.
ecef2enuECEF to ENU conversion.
- pytcl.coordinate_systems.conversions.ecef2enu(ecef, lat_ref, lon_ref, ecef_ref=None)[source]
Convert ECEF coordinates to local ENU coordinates.
- Parameters:
- Returns:
enu – Local ENU coordinates [east, north, up] in meters.
- Return type:
ndarray
Examples
Convert an aircraft position from ECEF to local ENU frame:
>>> import numpy as np >>> from pytcl.coordinate_systems.conversions import ecef2enu, geodetic2ecef >>> # Reference point (airport at 38.9°N, 77.0°W) >>> lat_ref = np.radians(38.9) >>> lon_ref = np.radians(-77.0) >>> # Aircraft ECEF position >>> ecef_aircraft = np.array([1130000.0, -4830000.0, 3990000.0]) >>> enu = ecef2enu(ecef_aircraft, lat_ref, lon_ref) >>> enu.shape (3,)
- pytcl.coordinate_systems.conversions.enu2ecef(enu, lat_ref, lon_ref, ecef_ref=None)[source]
Convert local ENU coordinates to ECEF coordinates.
- Parameters:
- Returns:
ecef – ECEF coordinates [x, y, z] in meters.
- Return type:
ndarray
Examples
Convert local ENU offset to ECEF coordinates:
>>> import numpy as np >>> from pytcl.coordinate_systems.conversions import enu2ecef >>> # Reference point (airport at 38.9°N, 77.0°W) >>> lat_ref = np.radians(38.9) >>> lon_ref = np.radians(-77.0) >>> # Aircraft 1km east, 2km north, 500m up >>> enu = np.array([1000.0, 2000.0, 500.0]) >>> ecef = enu2ecef(enu, lat_ref, lon_ref) >>> ecef.shape (3,)
See also
ecef2enuInverse conversion.
- pytcl.coordinate_systems.conversions.ecef2ned(ecef, lat_ref, lon_ref, ecef_ref=None)[source]
Convert ECEF coordinates to local NED (North-East-Down) coordinates.
- Parameters:
- Returns:
ned – Local NED coordinates [north, east, down] in meters.
- Return type:
ndarray
Examples
Convert aircraft ECEF position to local NED frame:
>>> import numpy as np >>> from pytcl.coordinate_systems.conversions import ecef2ned, geodetic2ecef >>> # Reference point (airport) >>> lat_ref = np.radians(38.9) >>> lon_ref = np.radians(-77.0) >>> # Aircraft ECEF position >>> ecef_aircraft = np.array([1130000.0, -4830000.0, 3990000.0]) >>> ned = ecef2ned(ecef_aircraft, lat_ref, lon_ref) >>> ned.shape # [north, east, down] (3,)
- pytcl.coordinate_systems.conversions.ned2ecef(ned, lat_ref, lon_ref, ecef_ref=None)[source]
Convert local NED coordinates to ECEF coordinates.
- Parameters:
- Returns:
ecef – ECEF coordinates [x, y, z] in meters.
- Return type:
ndarray
Examples
>>> import numpy as np >>> from pytcl.coordinate_systems import ned2ecef, geodetic2ecef >>> # Reference point: Kennedy Space Center (28.5°N, 80.65°W) >>> lat_ref = np.radians(28.5) >>> lon_ref = np.radians(-80.65) >>> # NED offset from reference: 5 km north, 3 km east, 1 km down >>> ned = np.array([5000.0, 3000.0, 1000.0]) >>> # Convert to ECEF coordinates >>> ecef = ned2ecef(ned, lat_ref, lon_ref) >>> ecef.shape (3,) >>> # Verify roundtrip conversion >>> from pytcl.coordinate_systems import ecef2ned >>> ned_back = ecef2ned(ecef, lat_ref, lon_ref) >>> np.allclose(ned, ned_back, atol=0.1) # Allow small numerical error True
See also
ecef2nedInverse conversion.
- pytcl.coordinate_systems.conversions.enu2ned(enu)[source]
Convert ENU coordinates to NED coordinates.
- Parameters:
enu (array_like) – ENU coordinates [east, north, up].
- Returns:
ned – NED coordinates [north, east, down].
- Return type:
ndarray
- pytcl.coordinate_systems.conversions.ned2enu(ned)[source]
Convert NED coordinates to ENU coordinates.
- Parameters:
ned (array_like) – NED coordinates [north, east, down].
- Returns:
enu – ENU coordinates [east, north, up].
- Return type:
ndarray
- pytcl.coordinate_systems.conversions.geocentric_radius(lat, a=6378137.0, f=0.0033528106647474805)[source]
Compute the geocentric radius at a given geodetic latitude.
- pytcl.coordinate_systems.conversions.prime_vertical_radius(lat, a=6378137.0, f=0.0033528106647474805)[source]
Compute the prime vertical radius of curvature.
- pytcl.coordinate_systems.conversions.meridional_radius(lat, a=6378137.0, f=0.0033528106647474805)[source]
Compute the meridional radius of curvature.
Spherical Coordinates
Spherical and polar coordinate conversions.
This module provides functions for converting between Cartesian and spherical/polar coordinate systems, following tracking conventions.
- pytcl.coordinate_systems.conversions.spherical.cart2sphere(cart_points, system_type='standard')[source]
Convert Cartesian coordinates to spherical coordinates.
- Parameters:
cart_points (array_like) – Cartesian coordinates. Can be: - Shape (3,) for a single point [x, y, z] - Shape (3, n) for n points (each column is a point) - Shape (n, 3) will be transposed automatically
system_type ({'standard', 'az-el', 'range-az-el'}, optional) – Spherical coordinate convention: - ‘standard’: Physics convention (r, θ polar from +z, φ azimuth from +x) - ‘az-el’: Tracking convention (r, azimuth from +x, elevation from xy-plane) - ‘range-az-el’: Same as ‘az-el’ (alias) Default is ‘standard’.
- Returns:
r (ndarray) – Range (radial distance from origin).
az (ndarray) – Azimuth angle in radians. - ‘standard’: Angle in xy-plane from +x axis [0, 2π) - ‘az-el’: Angle in xy-plane from +x axis [-π, π]
el (ndarray) – Elevation/polar angle in radians. - ‘standard’: Polar angle from +z axis [0, π] - ‘az-el’: Elevation from xy-plane [-π/2, π/2]
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> x, y, z = 1.0, 1.0, 1.0 >>> r, az, el = cart2sphere([x, y, z], system_type='az-el') >>> r 1.7320508075688772 >>> np.degrees(az) 45.0 >>> np.degrees(el) 35.26438968275465
See also
sphere2cartInverse conversion.
- pytcl.coordinate_systems.conversions.spherical.sphere2cart(r, az, el, system_type='standard')[source]
Convert spherical coordinates to Cartesian coordinates.
- Parameters:
r (array_like) – Range (radial distance).
az (array_like) – Azimuth angle in radians.
el (array_like) – Elevation/polar angle in radians.
system_type ({'standard', 'az-el', 'range-az-el'}, optional) – Spherical coordinate convention (see cart2sphere).
- Returns:
cart_points – Cartesian coordinates of shape (3,) or (3, n).
- Return type:
ndarray
Examples
>>> r, az, el = 1.732, np.radians(45), np.radians(35.26) >>> cart2sphere(sphere2cart(r, az, el, 'az-el'), 'az-el') (1.732..., 0.785..., 0.615...)
See also
cart2sphereInverse conversion.
- pytcl.coordinate_systems.conversions.spherical.cart2pol(cart_points)[source]
Convert 2D Cartesian coordinates to polar coordinates.
- Parameters:
cart_points (array_like) – Cartesian coordinates. Can be: - Shape (2,) for a single point [x, y] - Shape (2, n) for n points - Shape (n, 2) will be transposed
- Returns:
r (ndarray) – Radial distance from origin.
theta (ndarray) – Angle in radians from +x axis, in range [-π, π].
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> r, theta = cart2pol([1, 1]) >>> r 1.4142135623730951 >>> np.degrees(theta) 45.0
See also
pol2cartInverse conversion.
- pytcl.coordinate_systems.conversions.spherical.pol2cart(r, theta)[source]
Convert polar coordinates to 2D Cartesian coordinates.
- Parameters:
r (array_like) – Radial distance.
theta (array_like) – Angle in radians from +x axis.
- Returns:
cart_points – Cartesian coordinates of shape (2,) or (2, n).
- Return type:
ndarray
Examples
>>> x, y = pol2cart(1.414, np.radians(45)) >>> x, y (0.999..., 0.999...)
See also
cart2polInverse conversion.
- pytcl.coordinate_systems.conversions.spherical.cart2cyl(cart_points)[source]
Convert 3D Cartesian coordinates to cylindrical coordinates.
- Parameters:
cart_points (array_like) – Cartesian coordinates [x, y, z].
- Returns:
rho (ndarray) – Radial distance in xy-plane.
phi (ndarray) – Azimuth angle in radians from +x axis.
z (ndarray) – Height (same as Cartesian z).
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> rho, phi, z = cart2cyl([1, 1, 5]) >>> rho 1.4142135623730951 >>> np.degrees(phi) 45.0 >>> z 5.0
See also
cyl2cartInverse conversion.
- pytcl.coordinate_systems.conversions.spherical.cyl2cart(rho, phi, z)[source]
Convert cylindrical coordinates to 3D Cartesian coordinates.
- Parameters:
rho (array_like) – Radial distance in xy-plane.
phi (array_like) – Azimuth angle in radians from +x axis.
z (array_like) – Height.
- Returns:
cart_points – Cartesian coordinates of shape (3,) or (3, n).
- Return type:
ndarray
Examples
>>> cart = cyl2cart(1.414, np.radians(45), 5.0) >>> cart array([1.00..., 1.00..., 5. ])
See also
cart2cylInverse conversion.
- pytcl.coordinate_systems.conversions.spherical.ruv2cart(r, u, v)[source]
Convert r-u-v (range, direction cosines) to Cartesian coordinates.
The r-u-v system uses direction cosines where: - u = cos(az) * cos(el) = x/r - v = sin(az) * cos(el) = y/r - w = sin(el) = z/r (derived from u, v)
- Parameters:
r (array_like) – Range.
u (array_like) – Direction cosine along x-axis.
v (array_like) – Direction cosine along y-axis.
- Returns:
cart_points – Cartesian coordinates.
- Return type:
ndarray
Examples
>>> # Target at 45 deg azimuth, 30 deg elevation, range 100 >>> az, el = np.radians(45), np.radians(30) >>> u = np.cos(az) * np.cos(el) >>> v = np.sin(az) * np.cos(el) >>> cart = ruv2cart(100, u, v) >>> cart array([61.23..., 61.23..., 50. ])
Notes
This representation is common in radar tracking systems.
- pytcl.coordinate_systems.conversions.spherical.cart2ruv(cart_points)[source]
Convert Cartesian coordinates to r-u-v (range, direction cosines).
- Parameters:
cart_points (array_like) – Cartesian coordinates [x, y, z].
- Returns:
r (ndarray) – Range.
u (ndarray) – Direction cosine along x-axis (x/r).
v (ndarray) – Direction cosine along y-axis (y/r).
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> r, u, v = cart2ruv([100, 0, 0]) >>> r, u, v (100.0, 1.0, 0.0) >>> r, u, v = cart2ruv([50, 50, 50]) >>> r 86.602...
See also
ruv2cartInverse conversion.
Geodetic Coordinates
Geodetic coordinate conversions.
This module provides functions for converting between geodetic (latitude, longitude, altitude) and Earth-centered coordinate systems (ECEF), as well as local tangent plane coordinates (ENU, NED).
- pytcl.coordinate_systems.conversions.geodetic.geodetic2ecef(lat, lon, alt, a=6378137.0, f=0.0033528106647474805)[source]
Convert geodetic coordinates to ECEF (Earth-Centered Earth-Fixed).
- Parameters:
lat (array_like) – Geodetic latitude in radians.
lon (array_like) – Geodetic longitude in radians.
alt (array_like) – Altitude above the reference ellipsoid in meters.
a (float, optional) – Semi-major axis of the reference ellipsoid in meters. Default is WGS84 value.
f (float, optional) – Flattening of the reference ellipsoid. Default is WGS84 value.
- Returns:
ecef – ECEF coordinates [x, y, z] in meters. Shape is (3,) for single point or (3, n) for multiple points.
- Return type:
ndarray
Examples
>>> lat, lon, alt = np.radians(45), np.radians(-75), 100.0 >>> ecef = geodetic2ecef(lat, lon, alt) >>> ecef / 1e6 # In millions of meters array([ 1.14..., -4.29..., 4.48...])
See also
ecef2geodeticInverse conversion.
- pytcl.coordinate_systems.conversions.geodetic.ecef2geodetic(ecef, a=6378137.0, f=0.0033528106647474805, method='iterative')[source]
Convert ECEF coordinates to geodetic coordinates.
- Parameters:
ecef (array_like) – ECEF coordinates [x, y, z] in meters.
a (float, optional) – Semi-major axis of the reference ellipsoid.
f (float, optional) – Flattening of the reference ellipsoid.
method (str, optional) – Algorithm to use: - ‘iterative’: Bowring’s iterative method (default) - ‘direct’: Closed-form solution (Vermeille’s method)
- Returns:
lat (ndarray) – Geodetic latitude in radians.
lon (ndarray) – Geodetic longitude in radians.
alt (ndarray) – Altitude above the ellipsoid in meters.
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> ecef = np.array([1.14e6, -4.29e6, 4.48e6]) >>> lat, lon, alt = ecef2geodetic(ecef) >>> np.degrees(lat), np.degrees(lon) (45.0..., -75.0...)
See also
geodetic2ecefInverse conversion.
- pytcl.coordinate_systems.conversions.geodetic.geodetic2enu(lat, lon, alt, lat_ref, lon_ref, alt_ref, a=6378137.0, f=0.0033528106647474805)[source]
Convert geodetic coordinates to local ENU (East-North-Up) coordinates.
- Parameters:
lat (array_like) – Geodetic latitude in radians.
lon (array_like) – Geodetic longitude in radians.
alt (array_like) – Altitude in meters.
lat_ref (float) – Reference point latitude in radians.
lon_ref (float) – Reference point longitude in radians.
alt_ref (float) – Reference point altitude in meters.
a (float, optional) – Semi-major axis of the reference ellipsoid.
f (float, optional) – Flattening of the reference ellipsoid.
- Returns:
enu – Local ENU coordinates [east, north, up] in meters.
- Return type:
ndarray
Examples
>>> import numpy as np >>> from pytcl.coordinate_systems import geodetic2enu >>> # Reference point: San Francisco Airport (-122.375°, 37.615°) >>> lat_ref = np.radians(37.615) >>> lon_ref = np.radians(-122.375) >>> alt_ref = 0 >>> # Target point: 2 km north, 1 km east of reference >>> # Approximate location using small offsets >>> lat_target = lat_ref + np.radians(0.01) >>> lon_target = lon_ref + np.radians(0.01) >>> alt_target = 100 # 100 m elevation >>> enu = geodetic2enu(lat_target, lon_target, alt_target, ... lat_ref, lon_ref, alt_ref) >>> # ENU coordinates should show positive north and east offsets >>> enu[0] > 0 and enu[1] > 0 # East > 0, North > 0 True >>> enu[2] > 100 # Up should be approximately the altitude difference True
See also
enu2geodeticInverse conversion.
ecef2enuECEF to ENU conversion.
- pytcl.coordinate_systems.conversions.geodetic.ecef2enu(ecef, lat_ref, lon_ref, ecef_ref=None)[source]
Convert ECEF coordinates to local ENU coordinates.
- Parameters:
- Returns:
enu – Local ENU coordinates [east, north, up] in meters.
- Return type:
ndarray
Examples
Convert an aircraft position from ECEF to local ENU frame:
>>> import numpy as np >>> from pytcl.coordinate_systems.conversions import ecef2enu, geodetic2ecef >>> # Reference point (airport at 38.9°N, 77.0°W) >>> lat_ref = np.radians(38.9) >>> lon_ref = np.radians(-77.0) >>> # Aircraft ECEF position >>> ecef_aircraft = np.array([1130000.0, -4830000.0, 3990000.0]) >>> enu = ecef2enu(ecef_aircraft, lat_ref, lon_ref) >>> enu.shape (3,)
- pytcl.coordinate_systems.conversions.geodetic.enu2ecef(enu, lat_ref, lon_ref, ecef_ref=None)[source]
Convert local ENU coordinates to ECEF coordinates.
- Parameters:
- Returns:
ecef – ECEF coordinates [x, y, z] in meters.
- Return type:
ndarray
Examples
Convert local ENU offset to ECEF coordinates:
>>> import numpy as np >>> from pytcl.coordinate_systems.conversions import enu2ecef >>> # Reference point (airport at 38.9°N, 77.0°W) >>> lat_ref = np.radians(38.9) >>> lon_ref = np.radians(-77.0) >>> # Aircraft 1km east, 2km north, 500m up >>> enu = np.array([1000.0, 2000.0, 500.0]) >>> ecef = enu2ecef(enu, lat_ref, lon_ref) >>> ecef.shape (3,)
See also
ecef2enuInverse conversion.
- pytcl.coordinate_systems.conversions.geodetic.ecef2ned(ecef, lat_ref, lon_ref, ecef_ref=None)[source]
Convert ECEF coordinates to local NED (North-East-Down) coordinates.
- Parameters:
- Returns:
ned – Local NED coordinates [north, east, down] in meters.
- Return type:
ndarray
Examples
Convert aircraft ECEF position to local NED frame:
>>> import numpy as np >>> from pytcl.coordinate_systems.conversions import ecef2ned, geodetic2ecef >>> # Reference point (airport) >>> lat_ref = np.radians(38.9) >>> lon_ref = np.radians(-77.0) >>> # Aircraft ECEF position >>> ecef_aircraft = np.array([1130000.0, -4830000.0, 3990000.0]) >>> ned = ecef2ned(ecef_aircraft, lat_ref, lon_ref) >>> ned.shape # [north, east, down] (3,)
- pytcl.coordinate_systems.conversions.geodetic.ned2ecef(ned, lat_ref, lon_ref, ecef_ref=None)[source]
Convert local NED coordinates to ECEF coordinates.
- Parameters:
- Returns:
ecef – ECEF coordinates [x, y, z] in meters.
- Return type:
ndarray
Examples
>>> import numpy as np >>> from pytcl.coordinate_systems import ned2ecef, geodetic2ecef >>> # Reference point: Kennedy Space Center (28.5°N, 80.65°W) >>> lat_ref = np.radians(28.5) >>> lon_ref = np.radians(-80.65) >>> # NED offset from reference: 5 km north, 3 km east, 1 km down >>> ned = np.array([5000.0, 3000.0, 1000.0]) >>> # Convert to ECEF coordinates >>> ecef = ned2ecef(ned, lat_ref, lon_ref) >>> ecef.shape (3,) >>> # Verify roundtrip conversion >>> from pytcl.coordinate_systems import ecef2ned >>> ned_back = ecef2ned(ecef, lat_ref, lon_ref) >>> np.allclose(ned, ned_back, atol=0.1) # Allow small numerical error True
See also
ecef2nedInverse conversion.
- pytcl.coordinate_systems.conversions.geodetic.enu2ned(enu)[source]
Convert ENU coordinates to NED coordinates.
- Parameters:
enu (array_like) – ENU coordinates [east, north, up].
- Returns:
ned – NED coordinates [north, east, down].
- Return type:
ndarray
- pytcl.coordinate_systems.conversions.geodetic.ned2enu(ned)[source]
Convert NED coordinates to ENU coordinates.
- Parameters:
ned (array_like) – NED coordinates [north, east, down].
- Returns:
enu – ENU coordinates [east, north, up].
- Return type:
ndarray
- pytcl.coordinate_systems.conversions.geodetic.geodetic2sez(lat, lon, alt, lat_ref, lon_ref, alt_ref, a=6378137.0, f=0.0033528106647474805)[source]
Convert geodetic coordinates to local SEZ (South-East-Zenith) coordinates.
SEZ is a horizon-relative coordinate frame where: - S (South) points in the southward direction - E (East) points in the eastward direction - Z (Zenith) points upward (away from Earth center)
- Parameters:
lat (array_like) – Geodetic latitude in radians.
lon (array_like) – Geodetic longitude in radians.
alt (array_like) – Altitude in meters.
lat_ref (float) – Reference point latitude in radians.
lon_ref (float) – Reference point longitude in radians.
alt_ref (float) – Reference point altitude in meters.
a (float, optional) – Semi-major axis of the reference ellipsoid.
f (float, optional) – Flattening of the reference ellipsoid.
- Returns:
sez – Local SEZ coordinates [south, east, zenith] in meters.
- Return type:
ndarray
See also
sez2geodeticInverse conversion.
ecef2sezECEF to SEZ conversion.
Notes
SEZ is equivalent to NED when azimuth is measured from south. Conversion: SEZ = [S, E, Z] = [NED[0], NED[1], -NED[2]]
Examples
>>> sez = geodetic2sez(lat, lon, alt, lat_ref, lon_ref, alt_ref)
- pytcl.coordinate_systems.conversions.geodetic.ecef2sez(ecef, lat_ref, lon_ref, ecef_ref=None)[source]
Convert ECEF coordinates to local SEZ coordinates.
- Parameters:
ecef (array_like) – ECEF coordinates [X, Y, Z] in meters, shape (3,) or (3, N).
lat_ref (float) – Reference point latitude in radians.
lon_ref (float) – Reference point longitude in radians.
ecef_ref (array_like, optional) – Reference ECEF position. If None, the reference point is at (lat_ref, lon_ref) with zero altitude.
- Returns:
sez – SEZ coordinates [south, east, zenith] in meters.
- Return type:
ndarray
See also
sez2ecefInverse conversion.
- pytcl.coordinate_systems.conversions.geodetic.sez2ecef(sez, lat_ref, lon_ref, ecef_ref=None)[source]
Convert local SEZ coordinates to ECEF coordinates.
- Parameters:
sez (array_like) – SEZ coordinates [south, east, zenith] in meters, shape (3,) or (3, N).
lat_ref (float) – Reference point latitude in radians.
lon_ref (float) – Reference point longitude in radians.
ecef_ref (array_like, optional) – Reference ECEF position. If None, the reference point is at (lat_ref, lon_ref) with zero altitude.
- Returns:
ecef – ECEF coordinates [X, Y, Z] in meters.
- Return type:
ndarray
See also
ecef2sezForward conversion.
- pytcl.coordinate_systems.conversions.geodetic.sez2geodetic(sez, lat_ref, lon_ref, alt_ref, a=6378137.0, f=0.0033528106647474805)[source]
Convert local SEZ coordinates to geodetic coordinates.
- Parameters:
sez (array_like) – SEZ coordinates [south, east, zenith] in meters.
lat_ref (float) – Reference point latitude in radians.
lon_ref (float) – Reference point longitude in radians.
alt_ref (float) – Reference point altitude in meters.
a (float, optional) – Semi-major axis.
f (float, optional) – Flattening.
- Returns:
lat (ndarray) – Geodetic latitude in radians.
lon (ndarray) – Geodetic longitude in radians.
alt (ndarray) – Altitude in meters.
- Return type:
Tuple[ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]], ndarray[tuple[Any, …], dtype[floating]]]
Examples
>>> import numpy as np >>> from pytcl.coordinate_systems import sez2geodetic, geodetic2sez >>> # Observer location: Arecibo Observatory (18.3°N, 66.75°W) >>> lat_ref = np.radians(18.3) >>> lon_ref = np.radians(-66.75) >>> alt_ref = 500 # m >>> # Observed satellite: 30 km south, 20 km east, 35 km zenith >>> sez = np.array([-30000.0, 20000.0, 35000.0]) >>> # Convert to geodetic coordinates >>> lat, lon, alt = sez2geodetic(sez, lat_ref, lon_ref, alt_ref) >>> # Verify roundtrip conversion >>> from pytcl.coordinate_systems import geodetic2sez >>> sez_back = geodetic2sez(lat, lon, alt, lat_ref, lon_ref, alt_ref) >>> np.allclose(sez, sez_back, atol=1.0) # Allow ~1m numerical error True
See also
geodetic2sezForward conversion.
- pytcl.coordinate_systems.conversions.geodetic.geocentric_radius(lat, a=6378137.0, f=0.0033528106647474805)[source]
Compute the geocentric radius at a given geodetic latitude.
- pytcl.coordinate_systems.conversions.geodetic.prime_vertical_radius(lat, a=6378137.0, f=0.0033528106647474805)[source]
Compute the prime vertical radius of curvature.
Rotations
Rotation representations and conversions.
This module provides: - Basic rotation matrices (rotx, roty, rotz) - Euler angle conversions - Quaternion operations - Axis-angle and Rodrigues representations - Rotation interpolation (SLERP)
- pytcl.coordinate_systems.rotations.rotx(angle)[source]
Create rotation matrix for rotation about the x-axis.
- Parameters:
angle (float) – Rotation angle in radians.
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> R = rotx(np.pi/2) # 90 degree rotation about x >>> R @ [0, 1, 0] # y-axis maps to z-axis array([0., 0., 1.])
- pytcl.coordinate_systems.rotations.roty(angle)[source]
Create rotation matrix for rotation about the y-axis.
- Parameters:
angle (float) – Rotation angle in radians.
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> R = roty(np.pi/2) # 90 degree rotation about y >>> R @ [1, 0, 0] # x-axis maps to -z-axis array([ 0., 0., -1.])
- pytcl.coordinate_systems.rotations.rotz(angle)[source]
Create rotation matrix for rotation about the z-axis.
- Parameters:
angle (float) – Rotation angle in radians.
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> R = rotz(np.pi/2) # 90 degree rotation about z >>> R @ [1, 0, 0] # x-axis maps to y-axis array([0., 1., 0.])
- pytcl.coordinate_systems.rotations.euler2rotmat(angles, sequence='ZYX')[source]
Convert Euler angles to rotation matrix.
- Parameters:
angles (array_like) – Three Euler angles in radians [angle1, angle2, angle3].
sequence (str, optional) – Rotation sequence (e.g., ‘ZYX’, ‘XYZ’, ‘ZXZ’). Default is ‘ZYX’ (aerospace convention: yaw-pitch-roll).
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> yaw, pitch, roll = np.radians([45, 30, 15]) >>> R = euler2rotmat([yaw, pitch, roll], 'ZYX')
Notes
The rotation is applied in the order specified by the sequence, from right to left. For ‘ZYX’: R = Rz(yaw) @ Ry(pitch) @ Rx(roll).
- pytcl.coordinate_systems.rotations.rotmat2euler(R, sequence='ZYX')[source]
Convert rotation matrix to Euler angles.
- Parameters:
R (array_like) – 3x3 rotation matrix.
sequence (str, optional) – Rotation sequence. Default is ‘ZYX’.
- Returns:
angles – Three Euler angles in radians.
- Return type:
ndarray
Examples
>>> R = rotz(np.radians(45)) @ roty(np.radians(30)) @ rotx(np.radians(15)) >>> angles = rotmat2euler(R, 'ZYX') >>> np.degrees(angles) array([45., 30., 15.])
Notes
May have singularities (gimbal lock) at certain angles.
- pytcl.coordinate_systems.rotations.axisangle2rotmat(axis, angle)[source]
Convert axis-angle representation to rotation matrix.
- Parameters:
axis (array_like) – Unit vector defining the rotation axis [ax, ay, az].
angle (float) – Rotation angle in radians.
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> axis = [0, 0, 1] # Z-axis >>> R = axisangle2rotmat(axis, np.pi/2) # 90 deg about Z >>> R @ [1, 0, 0] # x-axis maps to y-axis array([0., 1., 0.])
Notes
Uses Rodrigues’ rotation formula.
- pytcl.coordinate_systems.rotations.rotmat2axisangle(R)[source]
Convert rotation matrix to axis-angle representation.
- Parameters:
R (array_like) – 3x3 rotation matrix.
- Returns:
axis (ndarray) – Unit vector rotation axis.
angle (float) – Rotation angle in radians [0, π].
- Return type:
Examples
>>> R = rotz(np.pi/2) # 90 deg about Z >>> axis, angle = rotmat2axisangle(R) >>> axis # Z-axis array([0., 0., 1.]) >>> np.degrees(angle) 90.0
- pytcl.coordinate_systems.rotations.quat2rotmat(q)[source]
Convert quaternion to rotation matrix.
- Parameters:
q (array_like) – Quaternion [qw, qx, qy, qz] (scalar-first convention).
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> q = [1, 0, 0, 0] # Identity quaternion >>> quat2rotmat(q) array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]])
- pytcl.coordinate_systems.rotations.rotmat2quat(R)[source]
Convert rotation matrix to quaternion.
- Parameters:
R (array_like) – 3x3 rotation matrix.
- Returns:
q – Quaternion [qw, qx, qy, qz] (scalar-first, positive qw).
- Return type:
ndarray
Examples
>>> R = np.eye(3) # Identity rotation >>> rotmat2quat(R) array([1., 0., 0., 0.])
Notes
Uses Shepperd’s method for numerical stability.
- pytcl.coordinate_systems.rotations.euler2quat(angles, sequence='ZYX')[source]
Convert Euler angles to quaternion.
- Parameters:
angles (array_like) – Three Euler angles in radians.
sequence (str, optional) – Rotation sequence. Default is ‘ZYX’.
- Returns:
q – Quaternion [qw, qx, qy, qz].
- Return type:
ndarray
Examples
Convert yaw-pitch-roll angles to quaternion:
>>> import numpy as np >>> from pytcl.coordinate_systems.rotations import euler2quat >>> # 45° yaw, 30° pitch, 0° roll >>> angles = np.radians([45, 30, 0]) >>> q = euler2quat(angles, sequence='ZYX') >>> q.shape (4,) >>> np.abs(q[0]) > 0.5 # scalar part should be significant True
See also
quat2eulerInverse conversion.
euler2rotmatConvert to rotation matrix instead.
- pytcl.coordinate_systems.rotations.quat2euler(q, sequence='ZYX')[source]
Convert quaternion to Euler angles.
- Parameters:
q (array_like) – Quaternion [qw, qx, qy, qz].
sequence (str, optional) – Rotation sequence. Default is ‘ZYX’.
- Returns:
angles – Three Euler angles in radians.
- Return type:
ndarray
Examples
>>> q = [1, 0, 0, 0] # Identity quaternion >>> angles = quat2euler(q, 'ZYX') >>> np.allclose(angles, [0, 0, 0]) True
- pytcl.coordinate_systems.rotations.quat_multiply(q1, q2)[source]
Multiply two quaternions.
- Parameters:
q1 (array_like) – First quaternion [qw, qx, qy, qz].
q2 (array_like) – Second quaternion.
- Returns:
q – Product quaternion q1 * q2.
- Return type:
ndarray
Notes
Quaternion multiplication represents composition of rotations. q1 * q2 applies q2 first, then q1.
Examples
Combine two rotations using quaternion multiplication:
>>> import numpy as np >>> from pytcl.coordinate_systems.rotations import quat_multiply, euler2quat >>> # 90° rotation about Z, then 45° about X >>> q_z90 = euler2quat(np.radians([90, 0, 0]), 'ZYX') >>> q_x45 = euler2quat(np.radians([0, 0, 45]), 'ZYX') >>> q_combined = quat_multiply(q_z90, q_x45) >>> q_combined.shape (4,) >>> np.isclose(np.linalg.norm(q_combined), 1.0) # unit quaternion True
See also
quat_inverseCompute quaternion inverse.
quat_rotateRotate a vector by a quaternion.
- pytcl.coordinate_systems.rotations.quat_conjugate(q)[source]
Compute quaternion conjugate.
- Parameters:
q (array_like) – Quaternion [qw, qx, qy, qz].
- Returns:
q_conj – Conjugate quaternion [qw, -qx, -qy, -qz].
- Return type:
ndarray
Examples
>>> quat_conjugate([0.707, 0.707, 0, 0]) array([ 0.707, -0.707, -0. , -0. ])
- pytcl.coordinate_systems.rotations.quat_inverse(q)[source]
Compute quaternion inverse.
- Parameters:
q (array_like) – Quaternion [qw, qx, qy, qz].
- Returns:
q_inv – Inverse quaternion.
- Return type:
ndarray
Examples
>>> q = euler2quat(np.radians([45, 0, 0]), 'ZYX') >>> q_inv = quat_inverse(q) >>> quat_multiply(q, q_inv) # Should be identity array([1., 0., 0., 0.])
Notes
For unit quaternions, inverse equals conjugate.
- pytcl.coordinate_systems.rotations.quat_rotate(q, v)[source]
Rotate a vector using a quaternion.
- Parameters:
q (array_like) – Quaternion [qw, qx, qy, qz].
v (array_like) – Vector to rotate [x, y, z].
- Returns:
v_rot – Rotated vector.
- Return type:
ndarray
Examples
>>> # 90 degree rotation about z-axis >>> q = euler2quat(np.radians([90, 0, 0]), 'ZYX') >>> v = np.array([1.0, 0.0, 0.0]) >>> v_rot = quat_rotate(q, v) >>> v_rot # x-axis becomes y-axis array([0., 1., 0.])
Notes
Computes q * v * q^(-1) where v is treated as a pure quaternion.
- pytcl.coordinate_systems.rotations.slerp(q1, q2, t)[source]
Spherical linear interpolation between two quaternions.
- Parameters:
q1 (array_like) – Start quaternion.
q2 (array_like) – End quaternion.
t (float) – Interpolation parameter in [0, 1].
- Returns:
q – Interpolated quaternion.
- Return type:
ndarray
Examples
>>> q1 = np.array([1, 0, 0, 0]) # identity >>> q2 = euler2quat(np.radians([90, 0, 0]), 'ZYX') # 90 deg about z >>> q_mid = slerp(q1, q2, 0.5) # halfway = 45 deg >>> angles = quat2euler(q_mid, 'ZYX') >>> np.degrees(angles[0]) # yaw should be ~45 45.0...
- pytcl.coordinate_systems.rotations.rodrigues2rotmat(rvec)[source]
Convert Rodrigues vector to rotation matrix.
- Parameters:
rvec (array_like) – Rodrigues vector (axis * angle).
- Returns:
R – 3x3 rotation matrix.
- Return type:
ndarray
Examples
>>> rvec = [0, 0, np.pi/2] # 90 deg about Z >>> R = rodrigues2rotmat(rvec) >>> R @ [1, 0, 0] # x-axis maps to y-axis array([0., 1., 0.])
Notes
The Rodrigues vector encodes both the rotation axis and angle: rvec = axis * angle, where |rvec| = angle.
- pytcl.coordinate_systems.rotations.rotmat2rodrigues(R)[source]
Convert rotation matrix to Rodrigues vector.
- Parameters:
R (array_like) – 3x3 rotation matrix.
- Returns:
rvec – Rodrigues vector (axis * angle).
- Return type:
ndarray
Examples
>>> R = rotz(np.pi/2) # 90 deg about Z >>> rvec = rotmat2rodrigues(R) >>> np.linalg.norm(rvec) # magnitude is the angle 1.5707...
- pytcl.coordinate_systems.rotations.dcm_rate(R, omega)[source]
Compute the time derivative of a rotation matrix.
- Parameters:
R (array_like) – Current rotation matrix.
omega (array_like) – Angular velocity vector [wx, wy, wz] in body frame.
- Returns:
R_dot – Time derivative of R.
- Return type:
ndarray
Examples
>>> R = np.eye(3) >>> omega = [0, 0, 1] # 1 rad/s about Z >>> R_dot = dcm_rate(R, omega) >>> R_dot[0, 1] # Off-diagonal elements show rotation -1.0
Notes
R_dot = R @ skew(omega)
- pytcl.coordinate_systems.rotations.is_rotation_matrix(R, tol=1e-06)[source]
Check if a matrix is a valid rotation matrix.
- Parameters:
R (array_like) – Matrix to check.
tol (float, optional) – Tolerance for numerical checks.
- Returns:
valid – True if R is a valid rotation matrix.
- Return type:
Examples
>>> R = rotx(np.pi/4) >>> is_rotation_matrix(R) True >>> is_rotation_matrix(np.eye(3) * 2) # not orthonormal False
Jacobians
Jacobian matrices for coordinate transformations.
This module provides: - Jacobians for spherical/Cartesian transformations - Jacobians for polar transformations - Jacobians for r-u-v direction cosines - Jacobians for ECEF/ENU/NED transformations - Jacobians for geodetic transformations - Covariance transformation utilities
- pytcl.coordinate_systems.jacobians.spherical_jacobian(cart_point, system_type='az-el')[source]
Compute Jacobian of Cartesian to spherical transformation.
Returns the Jacobian matrix J where d[r, az, el] = J @ d[x, y, z].
- Parameters:
cart_point (array_like) – Cartesian coordinates [x, y, z].
system_type ({'standard', 'az-el'}, optional) – Spherical coordinate convention. Default is ‘az-el’.
- Returns:
J – 3x3 Jacobian matrix.
- Return type:
ndarray
Notes
For tracking convention (‘az-el’): - r = sqrt(x² + y² + z²) - az = atan2(y, x) - el = atan2(z, sqrt(x² + y²))
Examples
>>> J = spherical_jacobian([1, 1, 1]) >>> J.shape (3, 3)
- pytcl.coordinate_systems.jacobians.spherical_jacobian_inv(r, az, el, system_type='az-el')[source]
Compute Jacobian of spherical to Cartesian transformation.
Returns the Jacobian matrix J where d[x, y, z] = J @ d[r, az, el].
- pytcl.coordinate_systems.jacobians.polar_jacobian(cart_point)[source]
Compute Jacobian of 2D Cartesian to polar transformation.
Returns J where d[r, theta] = J @ d[x, y].
- Parameters:
cart_point (array_like) – Cartesian coordinates [x, y].
- Returns:
J – 2x2 Jacobian matrix.
- Return type:
ndarray
- pytcl.coordinate_systems.jacobians.polar_jacobian_inv(r, theta)[source]
Compute Jacobian of polar to 2D Cartesian transformation.
Returns J where d[x, y] = J @ d[r, theta].
- pytcl.coordinate_systems.jacobians.ruv_jacobian(cart_point)[source]
Compute Jacobian of Cartesian to r-u-v transformation.
The r-u-v representation uses range and direction cosines: - u = x/r (direction cosine along x) - v = y/r (direction cosine along y)
- Parameters:
cart_point (array_like) – Cartesian coordinates [x, y, z].
- Returns:
J – 3x3 Jacobian matrix where d[r, u, v] = J @ d[x, y, z].
- Return type:
ndarray
- pytcl.coordinate_systems.jacobians.enu_jacobian(lat, lon)[source]
Compute Jacobian of ECEF to ENU transformation.
Returns J where d[e, n, u] = J @ d[x, y, z].
- Parameters:
- Returns:
J – 3x3 rotation matrix (Jacobian is constant for this linear transformation).
- Return type:
ndarray
Notes
Uses cached computation with quantized inputs for performance.
- pytcl.coordinate_systems.jacobians.ned_jacobian(lat, lon)[source]
Compute Jacobian of ECEF to NED transformation.
- Parameters:
- Returns:
J – 3x3 rotation matrix.
- Return type:
ndarray
Notes
Uses cached computation with quantized inputs for performance.
- pytcl.coordinate_systems.jacobians.geodetic_jacobian(lat, lon, alt, a=6378137.0, f=0.0033528106647474805)[source]
Compute Jacobian of geodetic to ECEF transformation.
Returns J where d[x, y, z] = J @ d[lat, lon, alt].
- Parameters:
- Returns:
J – 3x3 Jacobian matrix.
- Return type:
ndarray
- pytcl.coordinate_systems.jacobians.cross_covariance_transform(J, P)[source]
Transform a covariance matrix through a Jacobian.
Computes P_new = J @ P @ J.T for error propagation.
- Parameters:
J (array_like) – Jacobian matrix of the transformation.
P (array_like) – Original covariance matrix.
- Returns:
P_new – Transformed covariance matrix.
- Return type:
ndarray
Examples
>>> # Transform spherical covariance to Cartesian >>> P_sph = np.diag([1, 0.01, 0.01]) # [r, az, el] variances >>> r, az, el = 1000, np.radians(45), np.radians(30) >>> J = spherical_jacobian_inv(r, az, el) >>> P_cart = cross_covariance_transform(J, P_sph)
- pytcl.coordinate_systems.jacobians.numerical_jacobian(func, x, dx=1e-07)[source]
Compute Jacobian numerically using central differences.
- Parameters:
func (callable) – Function f(x) -> y.
x (array_like) – Point at which to compute Jacobian.
dx (float, optional) – Step size for finite differences.
- Returns:
J – Jacobian matrix.
- Return type:
ndarray