diff --git a/assets/README.md b/assets/README.md deleted file mode 100644 index f2016d9..0000000 --- a/assets/README.md +++ /dev/null @@ -1,41 +0,0 @@ -SatelittSim/assets/README.md -``` - -# Earth Texture for SatelittSim Visualization - -This directory should contain the NASA Blue Marble Earth texture for use in the 3D orbit visualization. - -## Downloading the Texture - -1. Visit the NASA Visible Earth website: - https://visibleearth.nasa.gov/images/57735/the-blue-marble-land-surface-ocean-color-sea-ice-and-clouds - -2. Download the highest resolution JPEG image available (typically 5400x2700 pixels) - -3. Save it to this directory as `earth_daymap.jpg` - -## Alternative Sources - -If the NASA link is unavailable, you can also use: - -- **Solar System Scope**: https://www.solarsystemscope.com/textures/ - - Download "Earth Day" texture - -- **Planet Pixel Emporium**: http://planetpixelemporium.com/earth.html - - Download the day/night texture - -## File Naming - -The visualization code looks for files in this order: -1. `earth_daymap.jpg` (preferred) -2. `earth_texture.jpg` (fallback) - -## Texture Features - -The NASA Blue Marble texture provides: -- High-resolution land surface imagery -- Ocean color data -- Sea ice coverage -- Cloud cover (optional versions available) - -This creates a realistic visualization of Earth from space during your satellite orbit simulations. \ No newline at end of file diff --git a/assets/earth_daymap.jpg b/assets/earth_daymap.jpg deleted file mode 100644 index 0a5c0bf..0000000 Binary files a/assets/earth_daymap.jpg and /dev/null differ diff --git a/examples/hypsotest.py b/examples/hypsotest.py new file mode 100644 index 0000000..4568de1 --- /dev/null +++ b/examples/hypsotest.py @@ -0,0 +1,94 @@ +"""Hypso 1 test: 1-month deorbit simulation without oxide calculations.""" + +import sys +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).parent.parent)) + +import numpy as np + +from src import Satellite, Simulation, SimulationConfig, State +from src.constants import EARTH_GRAVITATIONAL_PARAMETER, EARTH_RADIUS +from src.environment.atmosphere import Nrlmsise00Density +from src.events import DeorbitEvent, TimeLimitEvent +from src.forces import AtmosphericDrag, GravityGradientTorque, NewtonianGravity +from src.visualization import visualize_orbit + + +def get_default_stl_path() -> Path | None: + repo_root = Path(__file__).parent.parent + stl_file = repo_root / "sattelite.stl" + return stl_file if stl_file.exists() else None + + +def main(): + stl_path = get_default_stl_path() + sat = Satellite( + mass=7.6, + drag_area=0.06, + drag_coefficient=2.2, + min_drag_area=0.06, + max_drag_area=0.06, + stl_path=stl_path, + name="hypso 1", + ) + + altitude = 436_880 + expected_altitude = 395_977 + six_months = 182.625 * 86400 + + r = EARTH_RADIUS + altitude + v = np.sqrt(EARTH_GRAVITATIONAL_PARAMETER / r) + + state = State( + position=np.array([r, 0, 0]), + velocity=np.array([0.0, v, 0.1]), + time=0.0, + rotation_euler=np.array([0.5, 0.8, 0.3]), + angular_velocity_vec=np.array([1e-4, 1e-4, 1e-4]), + temperature_atmosphere=300.0, + temperature_sattelite=280.0, + oxide_thickness=0.0, + ) + + atmosphere = Nrlmsise00Density() + + forces = [ + NewtonianGravity(), + AtmosphericDrag(atmosphere), + GravityGradientTorque(stiffness=1e-10, damping=0.01), + ] + + events = [ + DeorbitEvent(), + TimeLimitEvent(max_time=six_months), + ] + + print(f"Hypso 1 - {altitude / 1000:.2f} km initial altitude") + print(f"Simulating for 6 months ({six_months / 86400:.1f} days)...") + sim = Simulation( + state, + sat, + forces, + events, + SimulationConfig(dt=3.0, save_interval=500), + ) + sim.run() + + final_alt_km = sim.final_state.altitude() / 1000 + expected_km = expected_altitude / 1000 + diff_km = final_alt_km - expected_km + + print(f"\nFinal altitude: {final_alt_km:.3f} km") + print(f"Expected altitude: {expected_km:.3f} km") + print(f"Diff: {diff_km:+.3f} km") + print(f"Sim time: {sim.final_state.time / 86400:.2f} days") + print(f"Steps: {sim.step_count}") + + if len(sim.history) > 1: + print("\nOpening 3D visualization...") + visualize_orbit(sim.history, sat, title="Hypso 1 Deorbit") + + +if __name__ == "__main__": + main() diff --git a/flake.nix b/flake.nix index 467a56c..2afa750 100644 --- a/flake.nix +++ b/flake.nix @@ -89,9 +89,9 @@ echo "Python: $(python --version)" echo "" echo "Run examples with:" - echo " python examples/basic_simulation.py # Deorbit simulation" - echo " python examples/deorbit_simple.py # With scheduled changes" - echo " python examples/orbit_comparison.py # Parallel comparison" + echo " python examples/basic_simulation.py # Deorbit simulation with 3D visualization" + echo " python examples/hypsotest.py # Hypso 1 6-month deorbit simulation" + echo " python examples/oxide_simulation.py # Oxide layer growth simulation" echo "" ''; }; diff --git a/main.py b/main.py deleted file mode 100644 index 92532ca..0000000 --- a/main.py +++ /dev/null @@ -1,210 +0,0 @@ -#!/usr/bin/env python3 -"""SatelittSim - Satellite Orbit Simulation with Visualization.""" - -from __future__ import annotations - -import argparse -import sys -from pathlib import Path -from typing import Optional - -import numpy as np - -from src import ( - Satellite, - State, - Simulation, - SimulationConfig, -) -from src.constants import EARTH_RADIUS, EARTH_GRAVITATIONAL_PARAMETER -from src.forces.gravity import NewtonianGravity -from src.forces.drag import AtmosphericDrag -from src.environment.atmosphere import ExponentialDensity -from src.events import DeorbitEvent, TimeLimitEvent - -HAS_NRLMSISE = False -Nrlmsise00Density = None -try: - from src.environment.atmosphere import Nrlmsise00Density - HAS_NRLMSISE = True -except ImportError: - pass - -HAS_POST_3D = False -visualize_orbit_fn = None -visualize_multiple_orbits_fn = None -try: - from src.visualization import visualize_orbit as visualize_orbit_fn - from src.visualization import visualize_multiple_orbits as visualize_multiple_orbits_fn - HAS_POST_3D = True -except ImportError: - pass - - -def create_parser() -> argparse.ArgumentParser: - parser = argparse.ArgumentParser( - description="SatelittSim - Satellite Orbit Simulation", - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - ) - - subparsers = parser.add_subparsers(dest="command", help="Simulation mode") - - sim_parser = subparsers.add_parser("simulate", help="Run orbit simulation") - sim_parser.add_argument("--altitude", "-a", type=float, default=400.0, help="Initial altitude in km") - sim_parser.add_argument("--mass", "-m", type=float, default=7.0, help="Satellite mass in kg") - sim_parser.add_argument("--area", type=float, default=0.05, help="Drag area in m^2") - sim_parser.add_argument("--drag-coeff", "-cd", type=float, default=2.2, help="Drag coefficient") - sim_parser.add_argument("--inclination", "-i", type=float, default=0.0, help="Orbital inclination in degrees") - density_choices = ["exponential"] + (["nrlmsise"] if HAS_NRLMSISE else []) - default_density = "nrlmsise" if HAS_NRLMSISE else "exponential" - sim_parser.add_argument("--density-model", choices=density_choices, default=default_density, help="Atmospheric density model") - sim_parser.add_argument("--dt", type=float, default=60.0, help="Base integration time step in seconds (adaptive)") - sim_parser.add_argument("--max-time", type=float, default=365.0, help="Maximum simulation time in days") - sim_parser.add_argument("--threshold", type=float, default=100.0, help="Deorbit altitude threshold in km") - sim_parser.add_argument("--save-interval", type=int, default=1000, help="Save state every N steps") - sim_parser.add_argument("--viz-3d", action="store_true", help="Show 3D visualization after simulation") - sim_parser.add_argument("--name", type=str, default="Satellite", help="Satellite name") - sim_parser.add_argument("--stl", type=str, default=None, help="Path to STL file for 3D satellite model") - - compare_parser = subparsers.add_parser("compare", help="Compare multiple orbits") - compare_parser.add_argument("--altitudes", type=float, nargs="+", default=[300.0, 400.0, 500.0], help="Altitudes to compare in km") - compare_parser.add_argument("--mass", type=float, default=7.0, help="Satellite mass in kg") - compare_parser.add_argument("--area", type=float, default=0.05, help="Drag area in m^2") - compare_parser.add_argument("--max-time", type=float, default=365.0, help="Maximum simulation time in days") - compare_parser.add_argument("--viz-3d", action="store_true", help="Show 3D visualization") - - return parser - - -def setup_initial_state(altitude_km: float, inclination_deg: float = 0.0) -> State: - altitude_m = altitude_km * 1000.0 - r = EARTH_RADIUS + altitude_m - v = np.sqrt(EARTH_GRAVITATIONAL_PARAMETER / r) - inc_rad = np.radians(inclination_deg) - position = np.array([r, 0.0, 0.0]) - velocity = np.array([0.0, v * np.cos(inc_rad), v * np.sin(inc_rad)]) - return State(position=position, velocity=velocity, time=0.0) - - -def get_density_model(name: str): - if name == "nrlmsise" and HAS_NRLMSISE and Nrlmsise00Density is not None: - return Nrlmsise00Density() - return ExponentialDensity() - - -def get_default_stl_path() -> Optional[Path]: - repo_root = Path(__file__).parent - stl_file = repo_root / "satellite.stl" - return stl_file if stl_file.exists() else None - - -def run_simulation( - altitude_km: float = 400.0, - mass: float = 7.0, - area: float = 0.05, - drag_coeff: float = 2.2, - inclination_deg: float = 0.0, - density_model: str = "exponential", - dt: float = 60.0, - max_time_days: float = 365.0, - threshold_km: float = 100.0, - save_interval: int = 1000, - name: str = "Satellite", - stl_path: Optional[Path] = None, -) -> Simulation: - if stl_path is None: - stl_path = get_default_stl_path() - sat = Satellite(mass=mass, drag_area=area, drag_coefficient=drag_coeff, name=name, stl_path=stl_path) - state = setup_initial_state(altitude_km, inclination_deg) - density = get_density_model(density_model) - - forces = [NewtonianGravity(), AtmosphericDrag(density)] - events = [DeorbitEvent(threshold_km * 1000), TimeLimitEvent(max_time_days * 24 * 3600)] - config = SimulationConfig(dt=dt, save_interval=save_interval) - - sim = Simulation(initial_state=state, satellite=sat, forces=forces, events=events, config=config) - sim.run() - - return sim - - -def main() -> int: - parser = create_parser() - args = parser.parse_args() - - if args.command is None: - parser.print_help() - return 0 - - if args.command == "simulate": - if args.viz_3d and not HAS_POST_3D: - print("Error: 3D post-visualization requires pygame. Install with: pip install pygame") - return 1 - - print(f"Starting simulation: {args.altitude} km altitude, {args.mass} kg satellite") - print(f"Density model: {args.density_model}") - - sim = run_simulation( - altitude_km=args.altitude, - mass=args.mass, - area=args.area, - drag_coeff=args.drag_coeff, - inclination_deg=args.inclination, - density_model=args.density_model, - dt=args.dt, - max_time_days=args.max_time, - threshold_km=args.threshold, - save_interval=args.save_interval, - name=args.name, - stl_path=Path(args.stl) if args.stl else None, - ) - - print(f"\nResults:") - print(f" Final altitude: {sim.final_state.altitude() / 1000:.2f} km") - print(f" Simulation time: {sim.final_state.time / 86400:.2f} days") - print(f" Steps: {sim.step_count}") - print(f" History points: {len(sim.history)}") - - if args.viz_3d and HAS_POST_3D and visualize_orbit_fn is not None: - print("\nOpening 3D visualization...") - visualize_orbit_fn(sim.history, sim.satellite, title=f"{args.name} Deorbit") - - elif args.command == "compare": - from src import run_parallel - - if args.viz_3d and not HAS_POST_3D: - print("Error: 3D visualization requires pygame. Install with: pip install pygame") - return 1 - - print(f"Comparing orbits at altitudes: {args.altitudes} km") - - default_stl = get_default_stl_path() - sims = [] - for alt in args.altitudes: - sat = Satellite(mass=args.mass, drag_area=args.area, name=f"{alt}km", stl_path=default_stl) - state = setup_initial_state(alt) - forces = [NewtonianGravity(), AtmosphericDrag(get_density_model("exponential"))] - events = [DeorbitEvent(), TimeLimitEvent(args.max_time * 24 * 3600)] - config = SimulationConfig(dt=10.0, save_interval=500) - sims.append(Simulation(state, sat, forces, events, config)) - - results = run_parallel(sims) - - print("\nResults:") - for alt, sim in zip(args.altitudes, results): - time_d = sim.final_state.time / 86400 - final_alt = sim.final_state.altitude() / 1000 - print(f" {alt} km: {time_d:.1f} days, final alt: {final_alt:.1f} km") - - if args.viz_3d and HAS_POST_3D and visualize_multiple_orbits_fn is not None: - visualize_multiple_orbits_fn( - [s.history for s in results], - [s.satellite for s in results], - labels=[f"{alt} km" for alt in args.altitudes], - ) - - return 0 - - -if __name__ == "__main__": - sys.exit(main())