transform_frames
Convert coordinates between reference frames (ECEF, ECI, ITRF, GCRS, GEODETIC) for aerospace applications. Specify source and target frames with optional epoch for time-dependent transformations.
Instructions
Transform coordinates between reference frames (ECEF, ECI, ITRF, GCRS, GEODETIC).
Args: coordinates: Dict with coordinate data (format depends on frame) from_frame: Source reference frame to_frame: Target reference frame epoch_utc: Optional epoch for time-dependent transformations (ISO format)
Returns: JSON string with transformed coordinates
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| coordinates | Yes | ||
| from_frame | Yes | ||
| to_frame | Yes | ||
| epoch_utc | No |
Implementation Reference
- aerospace_mcp/tools/frames.py:10-37 (handler)The MCP tool handler for 'transform_frames'. Accepts coordinates as dict, frame names, optional epoch, calls the underlying integration function, and returns formatted JSON output or error messages.def transform_frames( coordinates: dict, from_frame: Literal["ECEF", "ECI", "ITRF", "GCRS", "GEODETIC"], to_frame: Literal["ECEF", "ECI", "ITRF", "GCRS", "GEODETIC"], epoch_utc: str | None = None, ) -> str: """Transform coordinates between reference frames (ECEF, ECI, ITRF, GCRS, GEODETIC). Args: coordinates: Dict with coordinate data (format depends on frame) from_frame: Source reference frame to_frame: Target reference frame epoch_utc: Optional epoch for time-dependent transformations (ISO format) Returns: JSON string with transformed coordinates """ try: from ..integrations.frames import transform_frames as _transform result = _transform(coordinates, from_frame, to_frame, epoch_utc) return json.dumps(result, indent=2) except ImportError as e: return f"Frame transformation not available - missing dependency: {str(e)}" except Exception as e: logger.error(f"Frame transformation error: {str(e)}", exc_info=True) return f"Frame transformation error: {str(e)}"
- aerospace_mcp/fastmcp_server.py:95-95 (registration)Registers the transform_frames function as an MCP tool in the FastMCP server.mcp.tool(transform_frames)
- Core implementation of frame transformations using astropy if available, or manual methods for ECEF/GEODETIC and approximations for others.def transform_frames( xyz: list[float], from_frame: str, to_frame: str, epoch_iso: str = "2000-01-01T12:00:00", ) -> CoordinatePoint: """ Transform coordinates between reference frames. Args: xyz: [x, y, z] coordinates in meters from_frame: Source frame ("ECEF", "ECI", "ITRF", "GCRS") to_frame: Target frame ("ECEF", "ECI", "ITRF", "GCRS") epoch_iso: Reference epoch in ISO format Returns: CoordinatePoint with transformed coordinates """ if len(xyz) != 3: raise ValueError("xyz must be a list of 3 coordinates") x, y, z = xyz # Validate frame names valid_frames = {"ECEF", "ECI", "ITRF", "GCRS", "GEODETIC"} if from_frame not in valid_frames or to_frame not in valid_frames: raise ValueError(f"Frame must be one of {valid_frames}") # Same frame - no transformation needed if from_frame == to_frame: return CoordinatePoint(x=x, y=y, z=z, frame=to_frame, epoch=epoch_iso) # Use high-precision libraries if available if ASTROPY_AVAILABLE: try: # Parse epoch time = Time(epoch_iso, format="isot") # Map frame names to astropy frame_map = { "ECI": GCRS, "GCRS": GCRS, "ECEF": ITRS, "ITRF": ITRS, } if from_frame in frame_map and to_frame in frame_map: # Create coordinate object coords_from = frame_map[from_frame]( CartesianRepresentation(x=x * u.m, y=y * u.m, z=z * u.m), obstime=time, ) # Transform coords_to = coords_from.transform_to(frame_map[to_frame](obstime=time)) return CoordinatePoint( x=float(coords_to.cartesian.x.to(u.m).value), y=float(coords_to.cartesian.y.to(u.m).value), z=float(coords_to.cartesian.z.to(u.m).value), frame=to_frame, epoch=epoch_iso, ) except Exception: # Fall back to manual methods pass # Manual transformations for basic cases if from_frame == "ECEF" and to_frame == "GEODETIC": lat, lon, alt = _manual_ecef_to_geodetic(x, y, z) return CoordinatePoint(x=lat, y=lon, z=alt, frame="GEODETIC", epoch=epoch_iso) elif from_frame == "GEODETIC" and to_frame == "ECEF": x_new, y_new, z_new = _manual_geodetic_to_ecef(x, y, z) return CoordinatePoint(x=x_new, y=y_new, z=z_new, frame="ECEF", epoch=epoch_iso) elif (from_frame in ["ECI", "GCRS"] and to_frame in ["ECEF", "ITRF"]) or ( from_frame in ["ECEF", "ITRF"] and to_frame in ["ECI", "GCRS"] ): # Simple approximation: ECI ≈ ECEF (ignoring Earth rotation) # In real implementation, would apply rotation matrix based on GMST return CoordinatePoint(x=x, y=y, z=z, frame=to_frame, epoch=epoch_iso) raise NotImplementedError( f"Transformation from {from_frame} to {to_frame} not implemented. " f"Install astropy or skyfield for full functionality." )
- Pydantic models defining input/output schemas for coordinate transformations.class CoordinatePoint(BaseModel): """A point in 3D space with metadata.""" x: float = Field(..., description="X coordinate (m)") y: float = Field(..., description="Y coordinate (m)") z: float = Field(..., description="Z coordinate (m)") frame: str = Field(..., description="Coordinate frame") epoch: str | None = Field(None, description="Epoch (ISO format)") class GeodeticPoint(BaseModel): """Geodetic coordinates.""" latitude_deg: float = Field(..., description="Latitude in degrees") longitude_deg: float = Field(..., description="Longitude in degrees") altitude_m: float = Field(..., description="Height above ellipsoid (m)")