Skip to main content
Glama

porkchop_plot_analysis

Analyze optimal interplanetary transfer windows by generating porkchop plots for departure and arrival date combinations between celestial bodies.

Instructions

Generate porkchop plot for interplanetary transfer opportunities.

Args: departure_body: Departure celestial body name arrival_body: Arrival celestial body name departure_date_range: Range of departure dates (ISO format) arrival_date_range: Range of arrival dates (ISO format) analysis_options: Optional analysis settings

Returns: JSON string with porkchop plot data

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
departure_bodyYes
arrival_bodyYes
departure_date_rangeYes
arrival_date_rangeYes
analysis_optionsNo

Implementation Reference

  • Registration of the porkchop_plot_analysis tool in the FastMCP server.
    mcp.tool(porkchop_plot_analysis)
    mcp.tool(monte_carlo_uncertainty_analysis)
  • The MCP tool handler: a wrapper that attempts to delegate to trajopt.porkchop_plot_analysis (stubbed) and returns JSON or error message. Type hints define input schema.
    def porkchop_plot_analysis(
        departure_body: str,
        arrival_body: str,
        departure_date_range: list[str],
        arrival_date_range: list[str],
        analysis_options: dict | None = None,
    ) -> str:
        """Generate porkchop plot for interplanetary transfer opportunities.
    
        Args:
            departure_body: Departure celestial body name
            arrival_body: Arrival celestial body name
            departure_date_range: Range of departure dates (ISO format)
            arrival_date_range: Range of arrival dates (ISO format)
            analysis_options: Optional analysis settings
    
        Returns:
            JSON string with porkchop plot data
        """
        try:
            from ..integrations.trajopt import porkchop_plot_analysis as _porkchop
    
            result = _porkchop(
                departure_body,
                arrival_body,
                departure_date_range,
                arrival_date_range,
                analysis_options or {},
            )
    
            return json.dumps(result, indent=2)
    
        except ImportError:
            return (
                "Porkchop plot analysis not available - install space trajectory packages"
            )
        except Exception as e:
            logger.error(f"Porkchop plot error: {str(e)}", exc_info=True)
            return f"Porkchop plot error: {str(e)}"
  • Import of porkchop_plot_analysis from optimization module for server registration.
    from .tools.optimization import (
        genetic_algorithm_optimization,
        monte_carlo_uncertainty_analysis,
        optimize_thrust_profile,
        particle_swarm_optimization,
        porkchop_plot_analysis,
        trajectory_sensitivity_analysis,
    )
  • Comprehensive helper implementation of porkchop plot analysis logic, including planetary positions, Lambert transfers, C3 computation, and optimal transfer identification. Not directly linked to the registered tool.
    def porkchop_plot_analysis(
        departure_body: str = "Earth",
        arrival_body: str = "Mars",
        departure_dates: list[str] = None,
        arrival_dates: list[str] = None,
        min_tof_days: int = 100,
        max_tof_days: int = 400,
    ) -> dict[str, Any]:
        """
        Generate porkchop plot analysis for interplanetary transfers.
    
        Args:
            departure_body: Departure celestial body (default: Earth)
            arrival_body: Arrival celestial body (default: Mars)
            departure_dates: List of departure dates (ISO format)
            arrival_dates: List of arrival dates (ISO format)
            min_tof_days: Minimum time of flight (days)
            max_tof_days: Maximum time of flight (days)
    
        Returns:
            Dictionary containing transfer analysis grid
        """
        # Default date ranges if not provided
        if departure_dates is None:
            # Earth-Mars synodic period is ~26 months, sample over 2 years
            departure_dates = [
                "2025-01-01T00:00:00",
                "2025-03-01T00:00:00",
                "2025-05-01T00:00:00",
                "2025-07-01T00:00:00",
                "2025-09-01T00:00:00",
                "2025-11-01T00:00:00",
                "2026-01-01T00:00:00",
                "2026-03-01T00:00:00",
                "2026-05-01T00:00:00",
                "2026-07-01T00:00:00",
                "2026-09-01T00:00:00",
                "2026-11-01T00:00:00",
            ]
    
        if arrival_dates is None:
            # Generate arrival dates based on TOF constraints
            arrival_dates = [
                "2025-06-01T00:00:00",
                "2025-08-01T00:00:00",
                "2025-10-01T00:00:00",
                "2025-12-01T00:00:00",
                "2026-02-01T00:00:00",
                "2026-04-01T00:00:00",
                "2026-06-01T00:00:00",
                "2026-08-01T00:00:00",
                "2026-10-01T00:00:00",
                "2026-12-01T00:00:00",
                "2027-02-01T00:00:00",
                "2027-04-01T00:00:00",
            ]
    
        # Simplified planetary positions (circular orbits for demonstration)
        # In production, would use SPICE kernels or JPL ephemeris
        earth_orbit_radius = 1.0  # AU
        mars_orbit_radius = 1.524  # AU
        earth_period_days = 365.25
        mars_period_days = 686.98
    
        def get_planet_position(body: str, date_iso: str) -> list[float]:
            """Get simplified planet position at given date."""
            # Parse date (simplified)
            import datetime
    
            try:
                dt = datetime.datetime.fromisoformat(date_iso.replace("Z", "+00:00"))
            except Exception:
                dt = datetime.datetime.fromisoformat(date_iso)
    
            # Days since J2000
            j2000 = datetime.datetime(2000, 1, 1, 12, 0, 0)
            days_since_j2000 = (dt - j2000).total_seconds() / 86400
    
            if body.lower() == "earth":
                # Earth mean anomaly
                mean_anomaly = 2 * math.pi * days_since_j2000 / earth_period_days
                x = earth_orbit_radius * math.cos(mean_anomaly)
                y = earth_orbit_radius * math.sin(mean_anomaly)
                return [x * 1.496e11, y * 1.496e11, 0.0]  # Convert AU to meters
            elif body.lower() == "mars":
                # Mars mean anomaly
                mean_anomaly = 2 * math.pi * days_since_j2000 / mars_period_days
                x = mars_orbit_radius * math.cos(mean_anomaly)
                y = mars_orbit_radius * math.sin(mean_anomaly)
                return [x * 1.496e11, y * 1.496e11, 0.0]  # Convert AU to meters
            else:
                # Default to Earth position
                mean_anomaly = 2 * math.pi * days_since_j2000 / earth_period_days
                x = earth_orbit_radius * math.cos(mean_anomaly)
                y = earth_orbit_radius * math.sin(mean_anomaly)
                return [x * 1.496e11, y * 1.496e11, 0.0]
    
        # Generate porkchop data grid
        porkchop_data = []
    
        for dep_date in departure_dates:
            for arr_date in arrival_dates:
                # Calculate time of flight
                import datetime
    
                try:
                    dep_dt = datetime.datetime.fromisoformat(
                        dep_date.replace("Z", "+00:00")
                    )
                except Exception:
                    dep_dt = datetime.datetime.fromisoformat(dep_date)
    
                try:
                    arr_dt = datetime.datetime.fromisoformat(
                        arr_date.replace("Z", "+00:00")
                    )
                except Exception:
                    arr_dt = datetime.datetime.fromisoformat(arr_date)
    
                tof_days = (arr_dt - dep_dt).total_seconds() / 86400
    
                # Filter by TOF constraints
                if tof_days < min_tof_days or tof_days > max_tof_days:
                    continue
    
                # Get planetary positions
                r1 = get_planet_position(departure_body, dep_date)
                r2 = get_planet_position(arrival_body, arr_date)
    
                # Calculate transfer using simplified Lambert solver
                try:
                    mu_sun = 1.32712440018e20  # Sun's gravitational parameter (m³/s²)
                    tof_seconds = tof_days * 86400
    
                    # Use simplified Lambert solver
                    lambert_result = lambert_solver_simple(r1, r2, tof_seconds, mu_sun)
    
                    # Calculate characteristic energy (C3)
                    if "v1_vec_ms" in lambert_result:
                        v1 = lambert_result["v1_vec_ms"]
                    elif "initial_velocity_ms" in lambert_result:
                        v1 = lambert_result["initial_velocity_ms"]
                    else:
                        v1 = [0, 0, 0]  # Fallback
    
                    # Earth's orbital velocity at 1 AU
                    earth_v_orbit = math.sqrt(mu_sun / (1.496e11))  # m/s
    
                    # Excess velocity magnitude
                    v_inf_vec = [
                        v1[i] - earth_v_orbit if i == 1 else v1[i] for i in range(3)
                    ]
                    v_inf_mag = math.sqrt(sum(v**2 for v in v_inf_vec))
    
                    # Characteristic energy C3 (km²/s²)
                    c3 = (v_inf_mag / 1000) ** 2  # Convert to km/s then square
    
                    # Delta-V estimate (simplified)
                    delta_v_ms = v_inf_mag
    
                    porkchop_data.append(
                        {
                            "departure_date": dep_date,
                            "arrival_date": arr_date,
                            "time_of_flight_days": tof_days,
                            "c3_km2_s2": c3,
                            "delta_v_ms": delta_v_ms,
                            "departure_position_m": r1,
                            "arrival_position_m": r2,
                            "transfer_feasible": c3 < 100.0,  # Reasonable C3 limit
                        }
                    )
    
                except Exception as e:
                    # Add failed transfer case
                    porkchop_data.append(
                        {
                            "departure_date": dep_date,
                            "arrival_date": arr_date,
                            "time_of_flight_days": tof_days,
                            "c3_km2_s2": float("inf"),
                            "delta_v_ms": float("inf"),
                            "departure_position_m": r1,
                            "arrival_position_m": r2,
                            "transfer_feasible": False,
                            "error": str(e),
                        }
                    )
    
        # Find optimal transfer
        feasible_transfers = [t for t in porkchop_data if t["transfer_feasible"]]
    
        optimal_transfer = None
        if feasible_transfers:
            # Find minimum C3 transfer
            optimal_transfer = min(feasible_transfers, key=lambda x: x["c3_km2_s2"])
    
        # Generate summary statistics
        if feasible_transfers:
            c3_values = [t["c3_km2_s2"] for t in feasible_transfers]
            tof_values = [t["time_of_flight_days"] for t in feasible_transfers]
    
            summary_stats = {
                "feasible_transfers": len(feasible_transfers),
                "total_transfers_computed": len(porkchop_data),
                "min_c3_km2_s2": min(c3_values),
                "max_c3_km2_s2": max(c3_values),
                "mean_c3_km2_s2": sum(c3_values) / len(c3_values),
                "min_tof_days": min(tof_values),
                "max_tof_days": max(tof_values),
                "mean_tof_days": sum(tof_values) / len(tof_values),
            }
        else:
            summary_stats = {
                "feasible_transfers": 0,
                "total_transfers_computed": len(porkchop_data),
                "min_c3_km2_s2": None,
                "max_c3_km2_s2": None,
                "mean_c3_km2_s2": None,
                "min_tof_days": min_tof_days,
                "max_tof_days": max_tof_days,
                "mean_tof_days": (min_tof_days + max_tof_days) / 2,
            }
    
        return {
            "departure_body": departure_body,
            "arrival_body": arrival_body,
            "transfer_grid": porkchop_data,
            "optimal_transfer": optimal_transfer,
            "summary_statistics": summary_stats,
            "constraints": {
                "min_tof_days": min_tof_days,
                "max_tof_days": max_tof_days,
                "max_feasible_c3_km2_s2": 100.0,
            },
            "note": "Simplified analysis using circular planetary orbits. Use SPICE kernels for production applications.",
        }

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