# Utils/Conversion

Coordinate conversion and translation

## Import and use

Generally, we import the library itself:

```python
from gri_utils import conversion

xyz = conversion.lla_to_xyz(lla)
```

But to distinguish from other libraries, the [Google Style Guide](https://google.github.io/styleguide/pyguide.html#22-imports) also recommends giving it a more unique name, eg:

```python
from gri_utils import conversion as gri_conv

xyz = gri_conv.lla_to_xyz(lla)
```

## Units and data types

Each function is annotated with the units it requires and returns. Where possible, some functions are unitless and can be used with any consistent units.

Return types and arguments tend towards using numpy vectors (eg: `np.array((1,2,3))`) but can also use native lists, tuples, or other Sequences as arguments.

## Vectorization

Most conversion functions support vectorized inputs for efficient batch processing:

- **Single input**: Pass a shape `(3,)` array for a single position/vector
- **Batch input**: Pass a shape `(N, 3)` array for N positions/vectors

```python
# Single conversion
enu = xyz_to_enu(xyz_ref, xyz_vec)  # (3,) -> (3,)

# Batch conversion - N vectors at once
enu_vecs = xyz_to_enu(xyz_ref, xyz_vecs)  # (N, 3) -> (N, 3)
```

All vectorized functions use the same reference position for all conversions.

## Coordinate Transformations

### Rotation Matrices

The ECEF XYZ <=> ENU (or NED) rotation matrices are available via:

- `xyz_enu_rotation(xyz_pos)` - Get 3x3 rotation matrix from XYZ to ENU
- `enu_xyz_rotation(xyz_pos)` - Get 3x3 rotation matrix from ENU to XYZ
- `xyz_ned_rotation(xyz_pos)` - Get 3x3 rotation matrix from XYZ to NED
- `ned_xyz_rotation(xyz_pos)` - Get 3x3 rotation matrix from NED to XYZ

### Position Conversions

XYZ <=> LLA converts between an ECEF XYZ position in meters and a Latitude, Longitude, Altitude position in decimal degrees and meters.

```python
lla = xyz_to_lla(xyz)  # XYZ (m) -> LLA (deg, deg, m)
xyz = lla_to_xyz(lla)  # LLA (deg, deg, m) -> XYZ (m)
```

Both support vectorized inputs with shape `(N, 3)`.

## Delta Operations

For each delta type (ENU, NED, AER, CCR), there are four operations:

| Function | Description |
|----------|-------------|
| `get_*` | Get delta vector(s) between two XYZ positions |
| `translate_*` | Apply delta vector(s) to an XYZ position |
| `xyz_to_*` | Convert XYZ vector(s) to delta at a reference point |
| `*_to_xyz` | Convert delta vector(s) to XYZ at a reference point |

### Available Delta Types

- **ENU (East, North, Up)**: Unitless - same units in and out
- **NED (North, East, Down)**: Unitless - same units in and out
- **AER (Azimuth, Elevation, Range)**: Decimal degrees and meters
- **CCR (Clock, Cone, Range)**: Decimal degrees and meters

Example usage:

```python
# Get delta from pos1 to pos2
enu_vec = get_enu(xyz_pos1, xyz_pos2)

# Translate position by delta
xyz_pos2 = translate_enu(xyz_pos1, enu_vec)

# Convert XYZ vector to ENU at reference
enu_vec = xyz_to_enu(xyz_ref, xyz_vec)

# Convert ENU vector to XYZ at reference
xyz_vec = enu_to_xyz(xyz_ref, enu_vec)
```

All delta functions support vectorized inputs (shape `(N, 3)`) for batch processing.

## Covariance Matrix Transformations

For transforming 3x3 covariance matrices between coordinate frames, use the dedicated matrix functions:

| Function | Description |
|----------|-------------|
| `xyz_to_enu_cov` | Transform covariance matrix from XYZ to ENU |
| `enu_to_xyz_cov` | Transform covariance matrix from ENU to XYZ |
| `xyz_to_ned_cov` | Transform covariance matrix from XYZ to NED |
| `ned_to_xyz_cov` | Transform covariance matrix from NED to XYZ |

These functions apply the similarity transform `T @ M @ T.T` appropriate for covariance matrices.

```python
# Single matrix transformation
enu_cov = xyz_to_enu_cov(xyz_ref, xyz_cov)  # (3, 3) -> (3, 3)

# Batch matrix transformation
enu_covs = xyz_to_enu_cov(xyz_ref, xyz_covs)  # (N, 3, 3) -> (N, 3, 3)
```

**Note**: Do not use the vector functions (`xyz_to_enu`, etc.) for covariance matrices. They are designed for vectors only and will produce incorrect results for matrices.

## ECEF ↔ ECI Transformations

Convert between Earth-Centered Earth-Fixed (ECEF) and Earth-Centered Inertial (ECI) coordinate frames. ECEF rotates with the Earth while ECI is fixed relative to the stars.

```python
import time
from gri_utils.conversion import ecef_to_eci, eci_to_ecef

# Convert ECEF to ECI at current time
eci = ecef_to_eci(xyz_ecef, time.time())

# Convert back to ECEF
ecef = eci_to_ecef(xyz_eci, time.time())
```

Both functions support vectorized inputs (shape `(N, 3)`) and can accept either a single timestamp or an array of timestamps (one per point).

## Julian Date and GMST

Supporting functions for time-dependent coordinate transformations:

### Julian Date

```python
from gri_utils.conversion import unix_to_jd, jd_to_unix, datetime_to_jd, jd_to_datetime

# Unix timestamp to Julian Date
jd = unix_to_jd(time.time())

# Julian Date to Unix timestamp
unix_s = jd_to_unix(2451545.0)  # J2000.0 epoch

# Datetime conversions
jd = datetime_to_jd(dt)
dt = jd_to_datetime(jd)
```

### Greenwich Mean Sidereal Time (GMST)

```python
from gri_utils.conversion import unix_to_gmst, jd_to_gmst, datetime_to_gmst

# Get GMST in radians at current time
gmst = unix_to_gmst(time.time())

# From Julian Date
gmst = jd_to_gmst(2451545.0)

# From datetime
gmst = datetime_to_gmst(dt)
```

All time functions support vectorized inputs for batch processing.

## Degrees Transformation

This package also includes Degrees Minutes Seconds <=> Decimal Degrees conversions. It can convert to a `np.ndarray` of integer degrees, integer minutes, and decimal seconds or to a string of a common DMS format and back.

```python
dms = deg_to_dms(45.5)  # -> (45, 30, 0.0)
deg = dms_to_deg(45, 30, 0.0)  # -> 45.5

lat_str = deg_lat_to_dms_str(45.5)  # -> "45d 30' 0.0\" N"
lon_str = deg_lon_to_dms_str(-122.5)  # -> "122d 30' 0.0\" W"
```
