Skip to main content
Glama

elements_to_state_vector

Convert orbital elements (semi_major_axis, eccentricity, etc.) into a position and velocity state vector in the J2000 inertial frame for spacecraft trajectory calculations.

Instructions

Convert orbital elements to state vector in J2000 frame.

Args: orbital_elements: Dict with orbital elements (semi_major_axis_m, eccentricity, etc.)

Returns: JSON string with position [x,y,z] in meters and velocity [vx,vy,vz] in m/s in the J2000 inertial frame.

Raises: No exceptions are raised directly; errors are returned as formatted strings.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
orbital_elementsYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Core implementation of elements_to_state_vector conversion. Takes OrbitElements (Keplerian orbital elements), converts to Cartesian state vector in the J2000 inertial frame using the perifocal (PQW) frame approach with the 3-1-3 Euler angle rotation sequence (RAAN, inclination, argument of periapsis). Algorithm from Vallado (4th ed., Algorithm 10). Returns a StateVector dataclass with position_m, velocity_ms, epoch_utc, and frame='J2000'.
    def elements_to_state_vector(elements: OrbitElements) -> StateVector:
        """Convert classical orbital elements to a Cartesian state vector.
    
        Algorithm (Vallado, 4th ed., Algorithm 10):
            1. Compute the orbital radius from the conic equation.
            2. Express position and velocity in the perifocal (PQW) frame.
            3. Rotate from PQW to the J2000 inertial frame using the
               3-1-3 Euler angle rotation sequence (RAAN, inclination,
               argument of periapsis).
    
        Args:
            elements: Classical (Keplerian) orbital elements.
    
        Returns:
            State vector in the J2000 Earth-centered inertial frame.
        """
        # Convert angles to radians
        i = deg_to_rad(elements.inclination_deg)
        raan = deg_to_rad(elements.raan_deg)
        arg_pe = deg_to_rad(elements.arg_periapsis_deg)
        nu = deg_to_rad(elements.true_anomaly_deg)
    
        # Semi-major axis and eccentricity
        a = elements.semi_major_axis_m
        e = elements.eccentricity
    
        # Conic equation: r = p / (1 + e*cos(nu)), where p = a*(1 - e^2)
        # is the semi-latus rectum.
        r = a * (1 - e**2) / (1 + e * math.cos(nu))
    
        # Position in perifocal (PQW) coordinates: x along periapsis, y
        # perpendicular in the orbital plane, z = 0 (by definition).
        r_peri = [r * math.cos(nu), r * math.sin(nu), 0.0]
    
        # Semi-latus rectum p = a*(1 - e^2)
        p = a * (1 - e**2)
    
        # Velocity in perifocal coordinates derived from the vis-viva
        # relations in the PQW frame:
        #   v_P = -sqrt(mu/p) * sin(nu)
        #   v_Q =  sqrt(mu/p) * (e + cos(nu))
        v_peri = [
            -math.sqrt(MU_EARTH / p) * math.sin(nu),
            math.sqrt(MU_EARTH / p) * (e + math.cos(nu)),
            0.0,
        ]
    
        # Pre-compute trigonometric values for the rotation matrix
        cos_raan, sin_raan = math.cos(raan), math.sin(raan)
        cos_i, sin_i = math.cos(i), math.sin(i)
        cos_arg, sin_arg = math.cos(arg_pe), math.sin(arg_pe)
    
        # Rotation matrix [R] = R3(-RAAN) * R1(-i) * R3(-omega)
        # Transforms perifocal (PQW) -> J2000 inertial frame.
        # See Vallado (2013), Eq. 4-44.
        R11 = cos_raan * cos_arg - sin_raan * sin_arg * cos_i
        R12 = -cos_raan * sin_arg - sin_raan * cos_arg * cos_i
        R13 = sin_raan * sin_i
    
        R21 = sin_raan * cos_arg + cos_raan * sin_arg * cos_i
        R22 = -sin_raan * sin_arg + cos_raan * cos_arg * cos_i
        R23 = -cos_raan * sin_i
    
        R31 = sin_arg * sin_i
        R32 = cos_arg * sin_i
        R33 = cos_i
    
        # Apply rotation: r_J2000 = [R] * r_PQW
        r_j2000 = [
            R11 * r_peri[0] + R12 * r_peri[1] + R13 * r_peri[2],
            R21 * r_peri[0] + R22 * r_peri[1] + R23 * r_peri[2],
            R31 * r_peri[0] + R32 * r_peri[1] + R33 * r_peri[2],
        ]
    
        # Apply same rotation to velocity: v_J2000 = [R] * v_PQW
        v_j2000 = [
            R11 * v_peri[0] + R12 * v_peri[1] + R13 * v_peri[2],
            R21 * v_peri[0] + R22 * v_peri[1] + R23 * v_peri[2],
            R31 * v_peri[0] + R32 * v_peri[1] + R33 * v_peri[2],
        ]
    
        return StateVector(
            position_m=r_j2000,
            velocity_ms=v_j2000,
            epoch_utc=elements.epoch_utc,
            frame="J2000",
        )
  • Tool-level wrapper that accepts a dict (not OrbitElements), constructs OrbitElements from it, calls the integration-level _elements_to_state, and returns a JSON-formatted string with position and velocity. Handles ImportError (if orbital packages not installed) and general exceptions gracefully.
    def elements_to_state_vector(orbital_elements: dict) -> str:
        """Convert orbital elements to state vector in J2000 frame.
    
        Args:
            orbital_elements: Dict with orbital elements (semi_major_axis_m, eccentricity, etc.)
    
        Returns:
            JSON string with position [x,y,z] in meters and velocity [vx,vy,vz]
            in m/s in the J2000 inertial frame.
    
        Raises:
            No exceptions are raised directly; errors are returned as formatted strings.
        """
        try:
            from ..integrations.orbits import (
                OrbitElements,
            )
            from ..integrations.orbits import (
                elements_to_state_vector as _elements_to_state,
            )
    
            elements = OrbitElements(**orbital_elements)
            result = _elements_to_state(elements)
    
            return json.dumps(
                {
                    "position_m": {
                        "x": result.position_m[0],
                        "y": result.position_m[1],
                        "z": result.position_m[2],
                    },
                    "velocity_ms": {
                        "x": result.velocity_ms[0],
                        "y": result.velocity_ms[1],
                        "z": result.velocity_ms[2],
                    },
                    "reference_frame": "J2000",
                    "units": {"position": "meters", "velocity": "m/s"},
                },
                indent=2,
            )
    
        except ImportError:
            return "Orbital mechanics not available - install orbital packages"
        except Exception as e:
            logger.error(f"Elements to state vector error: {str(e)}", exc_info=True)
            return f"Elements to state vector error: {str(e)}"
  • OrbitElements dataclass defining the input schema for elements_to_state_vector. Fields: semi_major_axis_m, eccentricity, inclination_deg, raan_deg, arg_periapsis_deg, true_anomaly_deg, epoch_utc.
    @dataclass
    class OrbitElements:
        """Classical (Keplerian) orbital elements.
    
        The six classical orbital elements uniquely define a two-body conic
        orbit and the position of a body along that orbit at a given epoch.
    
        Attributes:
            semi_major_axis_m: Semi-major axis *a* (m). Defines orbit size.
            eccentricity: Eccentricity *e* (dimensionless, 0 = circle, <1 = ellipse).
            inclination_deg: Inclination *i* (degrees). Angle between the
                orbital plane and the equatorial plane.
            raan_deg: Right ascension of ascending node *Omega* (degrees).
                Angle in the equatorial plane from the vernal equinox to the
                ascending node.
            arg_periapsis_deg: Argument of periapsis *omega* (degrees). Angle
                in the orbital plane from the ascending node to periapsis.
            true_anomaly_deg: True anomaly *nu* (degrees). Angle in the
                orbital plane from periapsis to the current position.
            epoch_utc: Reference epoch in ISO-8601 UTC format.
        """
    
        semi_major_axis_m: float  # Semi-major axis (m)
        eccentricity: float  # Eccentricity (dimensionless)
        inclination_deg: float  # Inclination (degrees)
        raan_deg: float  # Right ascension of ascending node (degrees)
        arg_periapsis_deg: float  # Argument of periapsis (degrees)
        true_anomaly_deg: float  # True anomaly (degrees)
        epoch_utc: str  # Epoch in UTC ISO format
  • StateVector dataclass defining the output schema for elements_to_state_vector. Fields: position_m, velocity_ms, epoch_utc, frame (default 'J2000').
    @dataclass
    class StateVector:
        """Cartesian position and velocity state vector in an inertial frame.
    
        Attributes:
            position_m: Position vector [x, y, z] in meters.
            velocity_ms: Velocity vector [vx, vy, vz] in m/s.
            epoch_utc: Reference epoch in ISO-8601 UTC format.
            frame: Reference frame identifier (default ``"J2000"``).
        """
    
        position_m: list[float]  # Position vector [x, y, z] in meters
        velocity_ms: list[float]  # Velocity vector [vx, vy, vz] in m/s
        epoch_utc: str  # Epoch in UTC ISO format
        frame: str = "J2000"  # Reference frame
  • Registration of elements_to_state_vector as an MCP tool via mcp.tool() in the FastMCP server.
    mcp.tool(elements_to_state_vector)
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must carry the full burden. It mentions no direct exceptions but returns errors as formatted strings, which is useful. However, it does not disclose if the operation is read-only, requires authentication, or has side effects.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is structured as a docstring with Args, Returns, and Raises sections. It is informative without being verbose, but the Returns section could be more concise since it essentially repeats the purpose.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The description covers the return format (JSON string with position and velocity in J2000 frame) and error handling. Given the complexity of a conversion tool with a single nested parameter, it is fairly complete, though it could explicitly mention the coordinate frame in the Returns.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has one parameter 'orbital_elements' as an object with no property definitions (0% schema coverage). The description adds examples like 'semi_major_axis_m, eccentricity, etc.', which provides some meaning, but it's still vague and does not list all expected fields or their types.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool converts orbital elements to a state vector in the J2000 frame. It uses specific verb ('Convert') and resource ('orbital elements to state vector'), and is distinct from its inverse sibling 'state_vector_to_elements'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

No explicit guidance on when to use this tool versus alternatives like 'state_vector_to_elements' or other orbital tools. The description lacks context for ideal usage scenarios or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/cheesejaguar/aerospace-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server