# Feature: ENU Coordinate Management

## Description

Manages East-North-Up (ENU) coordinate system origins per flight and provides bidirectional conversions between GPS (WGS84) and ENU coordinates. ENU is a local Cartesian coordinate system used by the Factor Graph Optimizer for better numerical stability in optimization operations.

Each flight has its own ENU origin set from the flight's start_gps. All frames in a flight share the same origin for consistent coordinate conversions.

## Component APIs Implemented

### `set_enu_origin(flight_id: str, origin_gps: GPSPoint) -> None`
Sets the ENU origin for a specific flight during flight creation. Precomputes conversion factors for lat/lon to meters at origin latitude.

### `get_enu_origin(flight_id: str) -> GPSPoint`
Returns the stored ENU origin for a flight. Raises `OriginNotSetError` if called before set_enu_origin.

### `gps_to_enu(flight_id: str, gps: GPSPoint) -> Tuple[float, float, float]`
Converts GPS coordinates to ENU (east, north, up) in meters relative to flight's origin.

Algorithm:
- delta_lat = gps.lat - origin.lat
- delta_lon = gps.lon - origin.lon
- east = delta_lon × cos(origin.lat) × 111319.5
- north = delta_lat × 111319.5
- up = 0

### `enu_to_gps(flight_id: str, enu: Tuple[float, float, float]) -> GPSPoint`
Converts ENU coordinates back to GPS using the flight's ENU origin.

Algorithm:
- delta_lon = east / (cos(origin.lat) × 111319.5)
- delta_lat = north / 111319.5
- lat = origin.lat + delta_lat
- lon = origin.lon + delta_lon

## External Tools and Services

None - pure mathematical conversions.

## Internal Methods

### `_compute_meters_per_degree(latitude: float) -> Tuple[float, float]`
Precomputes conversion factors for a given latitude. Returns (meters_per_degree_lon, meters_per_degree_lat). Used during set_enu_origin for efficiency.

### `_get_origin_or_raise(flight_id: str) -> GPSPoint`
Helper to retrieve origin with error handling. Raises OriginNotSetError if not set.

## Unit Tests

### Test: set_enu_origin stores origin
- Call set_enu_origin with flight_id and origin GPS
- Verify origin is stored
- Verify conversion factors are precomputed

### Test: get_enu_origin returns stored origin
- Set origin via set_enu_origin
- Call get_enu_origin
- Verify returned GPS matches stored origin

### Test: get_enu_origin raises error if not set
- Call get_enu_origin for unknown flight_id
- Verify OriginNotSetError is raised

### Test: multiple flights have independent origins
- Set different origins for flight_1 and flight_2
- Verify each get_enu_origin returns correct independent origin

### Test: gps_to_enu at origin returns zero
- Set origin
- Convert origin GPS to ENU
- Verify result is (0, 0, 0)

### Test: gps_to_enu 1km east
- Set origin at known location
- Convert GPS point ~1km east of origin
- Verify east component ~1000m, north ~0

### Test: gps_to_enu 1km north
- Set origin at known location
- Convert GPS point ~1km north of origin
- Verify north component ~1000m, east ~0

### Test: gps_to_enu diagonal offset
- Convert GPS point with both lat/lon offset
- Verify correct east/north components

### Test: enu_to_gps at zero returns origin
- Set origin
- Convert (0, 0, 0) ENU to GPS
- Verify result matches origin GPS

### Test: round-trip conversion preserves coordinates
- Set origin
- Start with GPS point
- Convert to ENU via gps_to_enu
- Convert back via enu_to_gps
- Verify GPS matches original (within floating-point precision)

### Test: latitude affects east conversion factor
- Test gps_to_enu at different latitudes
- Verify east component accounts for cos(latitude) factor

## Integration Tests

### Test: ENU origin used by Factor Graph
- Create flight with start_gps via F02.1
- Verify F02.1 calls set_enu_origin with start_gps
- Verify Factor Graph can retrieve origin via get_enu_origin

### Test: ENU conversions consistent across components
- Set origin via set_enu_origin
- F10 Factor Graph uses gps_to_enu for absolute factors
- F10 uses enu_to_gps for trajectory output
- Verify GPS coordinates are consistent throughout pipeline

