diff --git a/docs/coding_standards.md b/docs/coding_standards.md new file mode 100644 index 0000000..2c11157 --- /dev/null +++ b/docs/coding_standards.md @@ -0,0 +1,343 @@ +# Coding Standards - Orbital Mechanics Simulation + +## Code Style + +- **C-style C++**: structs and functions only, NO classes or templates +- **File extensions**: Use `.cpp` extensions for all source files (for future C++ features if needed) +- **Include guards**: All headers use include guards (e.g., `#ifndef MODULE_NAME_H`) +- **Function design**: Small, focused functions with single responsibilities + +```cpp +// Header file with include guard +#ifndef ORBITAL_MECHANICS_H +#define ORBITAL_MECHANICS_H + +struct OrbitalElements { + double semi_major_axis; + double eccentricity; + double true_anomaly; +}; + +OrbitalElements propagate_orbital_elements(OrbitalElements elements, double dt, double parent_mass); + +#endif +``` + +## Naming Conventions + +**Struct names**: PascalCase +```cpp +struct CelestialBody { }; +struct OrbitalElements { }; +struct Spacecraft { }; +``` + +**Function names**: snake_case +```cpp +propagate_orbital_elements() +find_dominant_body() +update_bodies_physics() +``` + +**Member variables**: snake_case +```cpp +global_position +local_velocity +parent_index +``` + +**Constants**: UPPER_CASE +```cpp +PARABOLIC_TOLERANCE +MIN_MASS_RATIO +``` + +**Enum values**: UPPER_CASE with prefix +```cpp +enum BurnDirection { BURN_PROGRADE, BURN_RETROGRADE }; +enum TriggerType { TRIGGER_TIME, TRIGGER_TRUE_ANOMALY }; +``` + +## Comments + +- **Minimal comments**: Code should be self-documenting +- **Non-obvious logic only**: Comment only what cannot be inferred from the code +- **No decorative blocks**: No `===`, `---`, or ASCII art comment blocks + +```cpp +// Good: minimal comments +if (vec3_distance(pos, parent_pos) > soi_radius) { + return 0; +} + +// Bad: excessive decoration +// ======================================== +// SOI Check Function +// ======================================== +// This function checks if we're in SOI +// ======================================== +``` + +## Whitespace + +- **No trailing whitespace**: All files (including markdown) must not have trailing whitespace +- **Pre-commit hook**: Automatically strips trailing whitespace +- **Markdown line breaks**: Use `
` tag instead of two trailing spaces + +```markdown +Good: +Line 1
+Line 2 + +Bad: +Line 2 +Line 4 +``` + +## Memory Management + +- **Dynamic arrays**: Use `malloc` and `free` +- **Struct-based design**: No RAII or smart pointers +- **Manual cleanup**: Destroy functions handle cleanup + +```cpp +// Allocation +SimulationState* sim = malloc(sizeof(SimulationState)); +sim->bodies = malloc(max_bodies * sizeof(CelestialBody)); + +// Cleanup +void destroy_simulation(SimulationState* sim) { + free(sim->bodies); + free(sim->spacecraft); + free(sim); +} +``` + +## Code Organization + +**Layer separation**: Physics, Simulation, Configuration, Rendering layers + +**Physics module independence**: Physics functions use parameter-based signatures, no dependencies on simulation structures + +```cpp +// Good: parameter-based, independent +Vec3 vec3_add(Vec3 a, Vec3 b); +Mat3 mat3_rotation_orbital(double omega, double i, double omega_upper); + +// Avoid: depends on specific structures +Vec3 compute_global_position(CelestialBody* body); // Simulation layer +``` + +**Headers vs implementation**: +- Headers (`.h`): Declarations only +- Implementation (`.cpp`): Function definitions + +## Floating-Point Comparisons (Testing) + +- **Use `WithinAbs()`**: Required for floating-point comparisons +- **Avoid `Approx()`**: Deprecated in Catch2 +- **Required header**: `` + +```cpp +#include + +TEST_CASE("orbital energy conservation") { + double energy = calculate_total_energy(sim); + REQUIRE_THAT(energy, WithinAbs(expected, 1e-6)); +} +``` + +## Git Workflow + +- **Small commits**: Logical separation of changes +- **Concise messages**: One-line commit messages preferred +- **Build before commit**: Run `make && make test` before committing + +```bash +# Verify build and tests pass +make && make test + +# Commit with descriptive message +git add . +git commit -m "add parabolic orbit propagation" +``` + +## Build System + +**Makefile targets**: +- `make` or `make all`: Build simulation +- `make test`: Build and run automated tests +- `make clean`: Remove build artifacts +- `make rebuild`: Clean and rebuild + +**Default configuration**: +- Time step: 60 seconds for solar system scale +- Physics steps per frame: 100 (default) +- Simulation time per frame: 60s × 100 = 6000 seconds at 1× speed + +```bash +# Full build +make + +# Run tests +make test + +# Clean and rebuild +make rebuild +``` + +## Physics Considerations + +**Time step**: 60 seconds for solar system scale + +**Circular orbit velocity**: +```cpp +v = sqrt(G * M / r) +``` + +**Sphere of Influence (SOI)**: Hill sphere approximation +```cpp +r_soi = a * (m / M)^(2/5) +``` + +**SOI transitions**: 0.5× distance hysteresis to prevent oscillation + +**Parabolic orbits**: Escape velocity +```cpp +v² = 2GM / r +``` + +**Hyperbolic orbits**: Positive total energy with asymptotic velocity at infinity + +**Analytical propagation**: Uses Kepler equation solvers (not RK4 integration) +- Elliptical: E - e·sin(E) = M +- Parabolic: D + D³/3 = M (Barker's equation) +- Hyperbolic: H - e·sinh(H) = M + +## Module Dependencies + +``` +Physics (foundation, no dependencies) + ↓ +Orbital Mechanics (depends only on Physics) + ↓ +Spacecraft, Maneuver (depend on Physics) + ↓ +Simulation (depends on Physics, Orbital Mechanics, Spacecraft, Maneuver) + ↓ +Config Loader (depends on Simulation) +Config Validator (depends on Simulation and Orbital Mechanics) +Renderer (depends on Simulation) +UI Renderer (depends on Renderer and Simulation) +``` + +**Key principles**: +- Physics is completely independent +- Lower layers never depend on upper layers +- Each module has clear, minimal dependencies + +## Testing Guidelines + +- **Framework**: Catch2 +- **Run tests**: `make test` +- **Test configs**: Located in `tests/configs/` +- **Descriptive names**: Use clear test case names + +```bash +# Run all tests +make test + +# Run specific test config +./orbit_test '[config_name]' + +# Test with debug output (shows INFO messages) +./orbit_test -s '[config_name]' +``` + +**Test structure**: +```cpp +TEST_CASE("elliptical orbit energy conservation", "[energy]") { + SimulationState* sim = create_simulation(10, 1, 0, 60.0); + load_system_config(sim, "tests/configs/elliptical.toml"); + + double initial_energy = calculate_system_total_energy(sim); + + for (int i = 0; i < 100; i++) { + update_simulation(sim); + } + + double final_energy = calculate_system_total_energy(sim); + REQUIRE_THAT(final_energy, WithinAbs(initial_energy, 1e-6)); + + destroy_simulation(sim); +} +``` + +## Common Patterns + +**Struct initialization**: +```cpp +CelestialBody earth = {0}; +strcpy(earth.name, "Earth"); +earth.mass = 5.972e24; +earth.radius = 6.371e6; +``` + +**Error handling**: Return codes or NULL pointers +```cpp +int result = some_function(); +if (result != 0) { + return result; +} + +CelestialBody* body = find_body(sim, "Earth"); +if (body == NULL) { + return -1; +} +``` + +**Const correctness**: Use `const` for read-only parameters +```cpp +Vec3 vec3_add(const Vec3 a, const Vec3 b) { + Vec3 result = { a.x + b.x, a.y + b.y, a.z + b.z }; + return result; +} +``` + +## File Organization + +**Directory structure**: +``` +src/ + physics.h/cpp + orbital_mechanics.h/cpp + simulation.h/cpp + spacecraft.h/cpp + maneuver.h/cpp + config_loader.h/cpp + config_validator.h/cpp + renderer.h/cpp + ui_renderer.h/cpp + test_utilities.h/cpp + +tests/ + test_*.cpp + configs/ + +configs/ + *.toml + +docs/ + *.md +``` + +## Pre-commit Hooks + +The project uses a pre-commit hook that: +- Strips trailing whitespace from all files +- Prevents commits with trailing whitespace + +**If you see a "trailing whitespace" error**: +1. The hook has automatically fixed the files +2. Review the changes +3. Stage them again and retry the commit diff --git a/docs/config_format.md b/docs/config_format.md new file mode 100644 index 0000000..d8dd94b --- /dev/null +++ b/docs/config_format.md @@ -0,0 +1,490 @@ +# Config Format Reference + +## Overview + +Simulation configuration files use TOML format and are loaded via `load_system_config()` in config_loader.h. The config defines three main sections: + +- `[[bodies]]` - Celestial bodies (stars, planets, moons) +- `[[spacecraft]]` - Spacecraft orbiting bodies +- `[[maneuvers]]` - Impulsive burns for spacecraft + +## Bodies Configuration + +Each body entry defines a celestial object with physical properties and orbital elements. + +### Required Fields + +| Field | Type | Units | Description | +|-------|------|-------|-------------| +| `name` | string | - | Unique identifier for the body | +| `mass` | float | kg | Mass of the body | +| `radius` | float | m | Physical radius of the body | +| `parent_index` | integer | - | Index of parent body (-1 for root/star) | +| `color` | table | RGB | RGB color for rendering | + +### Orbit Table (nested) + +| Field | Type | Units | Default | Description | +|-------|------|-------|---------|-------------| +| `semi_major_axis` | float | m | 0.0 | Semi-major axis (elliptical/hyperbolic orbits only) | +| `semi_latus_rectum` | float | m | - | Semi-latus rectum (parabolic orbits only) | +| `eccentricity` | float | - | 0.0 | Orbital eccentricity (0-1 for elliptical, ~1 for parabolic, >1 for hyperbolic) | +| `true_anomaly` | float | rad | 0.0 | Initial true anomaly (position along orbit) | +| `inclination` | float | rad | 0.0 | Orbital plane inclination | +| `longitude_of_ascending_node` | float | rad | 0.0 | Longitude of ascending node | +| `argument_of_periapsis` | float | rad | 0.0 | Argument of periapsis | + +### Bodies Example + +```toml +# Root body (star) +[[bodies]] +name = "Sun" +mass = 1.989e30 +radius = 6.96e8 +parent_index = -1 +color = { r = 1.0, g = 1.0, b = 0.0 } +orbit = { + semi_major_axis = 0.0, + eccentricity = 0.0, + true_anomaly = 0.0 +} + +# Planet orbiting the star +[[bodies]] +name = "Earth" +mass = 5.972e24 +radius = 6.371e6 +parent_index = 0 +color = { r = 0.0, g = 0.5, b = 1.0 } +orbit = { + semi_major_axis = 1.496e11, + eccentricity = 0.0167, + inclination = 0.0, + longitude_of_ascending_node = 0.0, + argument_of_periapsis = 0.0, + true_anomaly = 0.0 +} + +# Moon orbiting the planet +[[bodies]] +name = "Moon" +mass = 7.342e22 +radius = 1.737e6 +parent_index = 1 +color = { r = 0.7, g = 0.7, b = 0.7 } +orbit = { + semi_major_axis = 3.844e8, + eccentricity = 0.0549, + inclination = 0.089, + true_anomaly = 0.0 +} +``` + +### Special Cases + +**Root Bodies:** Set `parent_index = -1`. Root bodies represent stars and have no parent. Their orbital elements should all be zero. + +**Parabolic Orbits:** When eccentricity ≈ 1.0 (within `PARABOLIC_TOLERANCE = 1e-3`), use `semi_latus_rectum` instead of `semi_major_axis`: +```toml +[[bodies]] +name = "Comet" +mass = 1.0e14 +radius = 5.0e3 +parent_index = 0 +color = { r = 0.7, g = 0.8, b = 0.9 } +orbit = { + semi_latus_rectum = 2.992e11, + eccentricity = 1.0, + true_anomaly = 0.0 +} +``` + +**Hyperbolic Orbits:** Use negative `semi_major_axis` for hyperbolic trajectories: +```toml +[[bodies]] +name = "InterstellarComet" +mass = 1.0e14 +radius = 5.0e3 +parent_index = 0 +color = { r = 0.5, g = 1.0, b = 0.5 } +orbit = { + semi_major_axis = -1.496e11, + eccentricity = 1.5, + true_anomaly = 0.0 +} +``` + +**3D Orbital Orientation:** Use inclination, longitude_of_ascending_node, and argument_of_periapsis for inclined orbits (Molniya example): +```toml +[[spacecraft]] +name = "Molniya_Satellite" +mass = 1000.0 +parent_index = 0 +orbit = { + semi_major_axis = 26540000.0, + eccentricity = 0.74, + true_anomaly = 0.0, + inclination = 1.107, + longitude_of_ascending_node = 0.0, + argument_of_periapsis = 4.71 +} +``` + +## Spacecraft Configuration + +Spacecraft are similar to bodies but lack physical radius and sphere of influence. They must orbit an existing body. + +### Required Fields + +| Field | Type | Units | Description | +|-------|------|-------|-------------| +| `name` | string | - | Unique identifier for the spacecraft | +| `mass` | float | kg | Mass of the spacecraft | +| `parent_index` | integer | - | Index of parent body (must reference a valid body) | + +### Orbit Table (nested) + +Same as bodies - see orbital elements table above. + +### Spacecraft Example + +```toml +[[spacecraft]] +name = "LEO_Satellite" +mass = 1000.0 +parent_index = 1 +orbit = { + semi_major_axis = 6.771e6, + eccentricity = 0.0, + true_anomaly = 0.0 +} +``` + +### Differences from Bodies + +- No `radius` field +- No `color` field +- No sphere of influence (SOI) +- `parent_index` must be a valid body index (cannot be -1) +- Cannot be a parent to other bodies or spacecraft + +## Maneuvers Configuration + +Maneuvers define impulsive burns that modify spacecraft velocity. Each maneuver specifies when and how to execute a burn. + +### Required Fields + +| Field | Type | Units | Description | +|-------|------|-------|-------------| +| `name` | string | - | Unique identifier for the maneuver | +| `spacecraft_name` | string | - | Name of the spacecraft to apply burn to | +| `direction` | string | - | Burn direction (see valid values below) | +| `delta_v` | float | m/s | Velocity change magnitude | +| `trigger_type` | string | - | Trigger condition type | +| `trigger_value` | float | varies | Trigger condition value | + +### Valid Direction Values + +| Direction | Description | +|-----------|-------------| +| `"prograde"` | Along velocity vector (increases orbital energy) | +| `"retrograde"` | Opposite velocity vector (decreases orbital energy) | +| `"normal"` | Along angular momentum vector (orbit normal) | +| `"antinormal"` | Opposite angular momentum vector | +| `"radial_in"` | Toward parent body | +| `"radial_out"` | Away from parent body | + +### Valid Trigger Types + +| Trigger Type | trigger_value | Description | +|--------------|----------------|-------------| +| `"time"` | seconds | Execute when simulation time >= trigger_value | +| `"true_anomaly"` | radians | Execute when spacecraft true anomaly within 0.01 rad of trigger_value | + +### Maneuvers Example + +```toml +# Time-based burn at 1 hour +[[maneuvers]] +name = "orbit_raise_1" +spacecraft_name = "LEO_Satellite" +trigger_type = "time" +trigger_value = 3600.0 +direction = "prograde" +delta_v = 500.0 + +# True anomaly-based burn at periapsis (ν = 0) +[[maneuvers]] +name = "periapsis_burn" +spacecraft_name = "LEO_Satellite" +trigger_type = "true_anomaly" +trigger_value = 0.0 +direction = "prograde" +delta_v = 1000.0 + +# Retrograde burn at apogee for orbit circularization +[[maneuvers]] +name = "circularize" +spacecraft_name = "LEO_Satellite" +trigger_type = "true_anomaly" +trigger_value = 3.14159 +direction = "retrograde" +delta_v = 500.0 +``` + +## Parsing Details + +### Parabolic vs Non-Parabolic Orbits + +The parser uses eccentricity to determine which orbit parameter is required: + +- **Parabolic (|e - 1.0| < 1e-3):** Requires `semi_latus_rectum`, `semi_major_axis` is ignored +- **Non-parabolic (e ≠ 1.0):** Requires `semi_major_axis`, `semi_latus_rectum` is ignored + +### Default Values + +Orbital elements default to 0.0 if not specified: +- `true_anomaly = 0.0` +- `inclination = 0.0` +- `longitude_of_ascending_node = 0.0` +- `argument_of_periapsis = 0.0` + +### Parent Index Behavior + +The `parent_index` field defines orbital hierarchy: +- `-1`: Root body (no parent, typically a star) +- `0` to N: Index of parent body in the bodies array +- Bodies must be ordered such that parents appear before children +- Spacecraft `parent_index` must reference a valid body index + +### Numeric Type Handling + +All numeric fields accept either integers or floating-point values: +- `mass`, `radius`, `semi_major_axis`, etc.: Accept int or float +- `parent_index`: Accepts int (preferred) or float (converted to int) +- Color components: Accept int or float (0-1 range typical) + +## Validation Rules + +The config validator (config_validator.cpp) runs automatically after loading. All validations must pass for the simulation to start. + +### Validation Constants + +| Constant | Value | Description | +|----------|-------|-------------| +| `MIN_MASS_RATIO` | 1000.0 | Minimum parent/child mass ratio for large bodies | +| `PARABOLIC_TOLERANCE` | 1e-3 | Tolerance for detecting parabolic orbits | +| `NESTED_ORBIT_FRACTION` | 5.0 | Max orbit radius as fraction of parent SOI | + +### Parent Index Ordering + +**Rule:** Body `parent_index` must be < body index or -1. + +**Purpose:** Ensures parents are defined before children in the config file. + +**Example Error:** +``` +Body 'Moon' (index 3) has invalid parent_index 3 - must be < 3 or -1 +``` + +**Spacecraft Validation:** Spacecraft `parent_index` must be a valid body index (0 to body_count-1). + +### Orbital Elements + +**Elliptical Orbits (e < 1):** +- `semi_major_axis` must be > 0 + +**Parabolic Orbits (|e - 1.0| < 1e-3):** +- `semi_latus_rectum` must be > 0 +- `semi_major_axis` is not used + +**Hyperbolic Orbits (e > 1):** +- `semi_major_axis` must be < 0 (negative for hyperbolic) +- `semi_major_axis` must not be 0 + +**General:** +- `eccentricity` must be >= 0 +- `semi_major_axis` or `semi_latus_rectum` must not be 0 + +### True Anomaly Ranges (Hyperbolic Orbits) + +**Rule:** For hyperbolic orbits (e > 1), true anomaly must be within valid asymptotic bounds. + +**Valid Range:** [0, arccos(-1/e)] ∪ [2π - arccos(-1/e), 2π] + +**Purpose:** Prevents specifying positions in the forbidden region of hyperbolic orbits. + +**Example Error:** +``` +Spacecraft 'Test' has invalid true_anomaly for hyperbolic orbit + Eccentricity: 1.5000 + True anomaly: 2.0000 rad + Valid range: [0, 2.3005] ∪ [3.9827, 2π] +``` + +### Initial Positions + +**Rule:** Distance between body and parent must exceed combined radii. + +**Purpose:** Prevents objects from starting inside their parent. + +**Example Error:** +``` +Body 'Satellite' (index 2) too close to parent 'Earth' (index 1) + Distance: 6.37e6 m + Minimum required: 6.47e6 m (parent radius + body radius) +``` + +**Spacecraft:** Distance must be >= parent radius (spacecraft has no radius). + +### Mass Ratios + +**Rule:** For root children with radius > 50% of parent, mass ratio >= 1000. + +**Purpose:** Ensures hierarchical system stability for large bodies. + +**Example Error:** +``` +Body 'LargeMoon' (mass=1.00e25 kg, radius=4.00e6 m) has insufficient mass ratio with root parent 'Earth' (mass=5.97e24 kg, radius=6.37e6 m) + Mass ratio: 0.60 (minimum required: 1000.00) + Radius ratio: 0.63 (triggers validation for radius > 50% of parent) +``` + +### SOI Overlap + +**Rule:** Bodies sharing the same parent must not have overlapping spheres of influence. + +**Purpose:** Prevents ambiguous SOI boundaries that could cause numerical instability. + +**Example Error:** +``` +Bodies 'MoonA' and 'MoonB' have overlapping SOIs while sharing same parent 'Planet' + Separation: 1.00e8 m + Combined SOI: 1.50e8 m (7.00e7 + 8.00e7) + SOI overlap: 5.00e7 m +``` + +### Nested Orbits + +**Rule:** Moon-like bodies must orbit within 5x of parent's SOI. + +**Applies to:** +- Bodies with radius < 30% of parent radius +- Bodies with eccentricity < 0.5 +- Bodies with mass < 1e20 kg + +**Purpose:** Ensures moon orbits are stable within parent's sphere of influence. + +**Example Error:** +``` +Body 'DistantMoon' orbit extends too far from parent 'Planet' + Child orbit radius: 2.00e9 m + Parent SOI radius: 1.00e9 m + Maximum allowed: 5.00e9 m (500.0% of parent SOI) +``` + +## Complete Example Config + +```toml +# Complete Solar System Example with Spacecraft and Maneuvers + +[[bodies]] +name = "Sun" +mass = 1.989e30 +radius = 6.96e8 +parent_index = -1 +color = { r = 1.0, g = 1.0, b = 0.0 } +orbit = { + semi_major_axis = 0.0, + eccentricity = 0.0, + true_anomaly = 0.0 +} + +[[bodies]] +name = "Earth" +mass = 5.972e24 +radius = 6.371e6 +parent_index = 0 +color = { r = 0.0, g = 0.5, b = 1.0 } +orbit = { + semi_major_axis = 1.496e11, + eccentricity = 0.0167, + inclination = 0.0, + longitude_of_ascending_node = 0.0, + argument_of_periapsis = 0.0, + true_anomaly = 0.0 +} + +[[bodies]] +name = "Moon" +mass = 7.342e22 +radius = 1.737e6 +parent_index = 1 +color = { r = 0.7, g = 0.7, b = 0.7 } +orbit = { + semi_major_axis = 3.844e8, + eccentricity = 0.0549, + inclination = 0.089, + true_anomaly = 0.0 +} + +[[spacecraft]] +name = "LEO_Satellite" +mass = 1000.0 +parent_index = 1 +orbit = { + semi_major_axis = 6.771e6, + eccentricity = 0.0, + true_anomaly = 0.0 +} + +[[spacecraft]] +name = "Molniya_Satellite" +mass = 1000.0 +parent_index = 1 +orbit = { + semi_major_axis = 26540000.0, + eccentricity = 0.74, + true_anomaly = 0.0, + inclination = 1.107, + longitude_of_ascending_node = 0.0, + argument_of_periapsis = 4.71 +} + +[[maneuvers]] +name = "orbit_raise" +spacecraft_name = "LEO_Satellite" +trigger_type = "time" +trigger_value = 3600.0 +direction = "prograde" +delta_v = 500.0 + +[[maneuvers]] +name = "periapsis_burn" +spacecraft_name = "Molniya_Satellite" +trigger_type = "true_anomaly" +trigger_value = 0.0 +direction = "prograde" +delta_v = 1000.0 +``` + +## Orbit Type Quick Reference + +| Eccentricity | Orbit Type | Parameter | Example | +|--------------|------------|-----------|---------| +| e = 0 | Circular | semi_major_axis | Planets, moons | +| 0 < e < 1 | Elliptical | semi_major_axis (positive) | Typical satellites | +| e ≈ 1 | Parabolic | semi_latus_rectum | Escape trajectories | +| e > 1 | Hyperbolic | semi_major_axis (negative) | Interstellar objects | + +## Tips + +1. **Units:** Always use SI units (kg, meters, seconds, radians) +2. **Angles:** Use radians for all angular values (inclination, true_anomaly, etc.) +3. **Ordering:** List bodies from star -> planets -> moons in order +4. **Testing:** Start with circular orbits (e=0) before adding complexity +5. **Validation:** Read error messages carefully - they indicate specific violations +6. **Precision:** Use scientific notation for large/small values (e.g., 1.496e11) +7. **Colors:** Use RGB values in [0, 1] range for realistic rendering +8. **Hyperbolic:** Remember true_anomaly limits for hyperbolic orbits diff --git a/docs/newton_raphson_propagation_plan.md b/docs/planning/newton_raphson_propagation_plan.md similarity index 100% rename from docs/newton_raphson_propagation_plan.md rename to docs/planning/newton_raphson_propagation_plan.md diff --git a/docs/session_summaries/2026-02-04-documentation-rewrite.md b/docs/session_summaries/2026-02-04-documentation-rewrite.md new file mode 100644 index 0000000..f76e415 --- /dev/null +++ b/docs/session_summaries/2026-02-04-documentation-rewrite.md @@ -0,0 +1,87 @@ +# Session Summary - Documentation Rewrite + +**Date:** 2026-02-04 + +## Session Overview +This session focused on rewriting the technical documentation to reflect the current state of the codebase after switching to analytical propagation. + +## Changes Made + +### 1. Explored codebase +Used @explore sub-agent to identify all critical data structures and functions across the codebase. + +### 2. Created new technical reference +Rewrote `docs/technical_reference.md` from scratch: +- Updated all references from RK4 to analytical propagation +- Added comprehensive data structure documentation with file locations +- Added detailed function signatures for critical functions +- Added module dependency graph (later removed) +- Added analytical propagation algorithm details for all orbit types +- Added velocity deviation detection (1e-6 tolerance) +- Added orbital element reconstruction after burns and SOI transitions +- Kept coordinate frame strategy, orbit types, and data flow sections +- Initially had line numbers, ASCII diagrams (later removed) +- Initial version: 730 lines + +### 3. Created config format documentation +New file `docs/config_format.md`: +- Complete TOML format documentation for bodies, spacecraft, maneuvers +- All field descriptions, types, units +- Parsing details and default values +- All 7 validation rules with constants +- Complete working example config +- 490 lines + +### 4. Created coding standards documentation +New file `docs/coding_standards.md`: +- Code style (C-style C++, no classes/templates) +- Naming conventions +- Comment guidelines +- Whitespace rules (no trailing whitespace) +- Memory management (malloc/free) +- Code organization and layer separation +- Floating-point comparisons for tests +- Git workflow +- Build system details +- Physics considerations +- Module dependencies +- Testing guidelines + +### 5. Simplified technical reference +- Moved verbose config section to config_format.md (replaced with brief summary) +- Moved config validator section to config_format.md (replaced with brief summary) +- Removed most line numbers (kept only 5 critical algorithm locations) +- Reduced from 655 lines to 557 lines + +### 6. Removed ASCII diagrams +- Removed module dependency graph (replaced with text) +- Removed data flow diagrams (replaced with text) +- Removed SOI mechanics flow chart (replaced with text) +- Reduced from 557 lines to 557 lines (text descriptions were concise) + +### 7. Cleaned up +- Removed `docs/technical_reference.md.backup` + +## Commits +No commits were made during this session (all documentation changes). + +## Results +- Created 2 new documentation files (config_format.md, coding_standards.md) +- Completely rewrote technical reference (730 lines → 557 lines) +- Documentation is now LLM-friendly (text-only, no ASCII diagrams) +- All content from original reference preserved, reorganized, and updated + +## Files Modified +- `docs/technical_reference.md` - Completely rewritten +- `docs/config_format.md` - Created (490 lines) +- `docs/coding_standards.md` - Created +- `docs/technical_reference.md.backup` - Removed + +## Next Steps +None - documentation rewrite is complete. + +## Notes +- Technical reference is now optimized for LLM agents to understand the codebase +- Line numbers kept only for 5 critical algorithms (unlikely to change) +- Detailed config format is in separate doc for easy reference +- Coding standards are documented for consistency diff --git a/docs/technical_reference.md b/docs/technical_reference.md index b574f0f..e8930bc 100644 --- a/docs/technical_reference.md +++ b/docs/technical_reference.md @@ -1,51 +1,15 @@ -# Orbital Mechanics Simulation - Technical Reference +# Technical Reference - Orbital Mechanics Simulation ## Overview -3D orbital mechanics simulation using 2-body gravitational model with sphere of influence (SOI) transitions. Built with C-style C++ and raylib. - -## Technical Constraints -- C-style C++ only: structs and functions, no classes or templates -- RK4 (Runge-Kutta 4th order) integration for physics -- Simple rotations (quaternions deferred) - -## Coordinate Frame Strategy -The simulation uses a hybrid coordinate frame system optimized for numerical precision in nested orbital systems. - -**Global Frame:** -- Origin at root body (index 0, parent_index = -1) -- All positions/velocities are in meters relative to this origin -- Used for SOI calculations, rendering, and interplanetary trajectories - -**Local Frame:** -- Position/velocity relative to gravitational parent -- Used for most bodies in stable nested orbits (moons around planets) -- RK4 integration operates in local frame to preserve floating-point precision - -**Coordinate Frame Selection:** -- Bodies using local frame: Children with stable orbital relationships (e.g., moons orbiting planets) -- Bodies using global frame: Direct children of root body (planets) and bodies on interplanetary trajectories -- Dual coordinate storage maintained for seamless frame transitions during SOI crossings - -**Benefits:** -- Eliminates large offsets in floating-point calculations (moon at 3.8×10⁸ m instead of 1.5×10¹¹ m) -- Isolates moon orbits from planetary perturbations -- Maintains full floating-point precision for small orbital changes -- Improved Earth-Moon orbital stability (20% drift → stable) - -**Key Functions:** -- `initialize_local_coordinates()` - initializes local frame positions/velocities from global coordinates -- `compute_global_coordinates()` - computes global positions/velocities from local frames -- `compute_spacecraft_globals()` - calculates global coordinates for all spacecraft - -**Implementation Details:** -- Dual coordinate storage: both local and global coordinates maintained -- Parent bodies treated as origin in child's reference frame during integration -- RK4 integration operates on local coordinates -- Global coordinates computed after each physics step for rendering and SOI checks +N-body orbital mechanics simulator using analytical propagation for precise Keplerian trajectories. Supports elliptical, parabolic, and hyperbolic orbits with SOI (Sphere of Influence) transitions, impulsive burns, and 3D visualization using Raylib. + +## Architecture +C-style C++ implementation (structs/functions, no classes/templates) with modular design. The system is organized into these interconnected modules: Main (entry point), Simulation (core loop), Orbital Mechanics (Keplerian propagation), Physics (vector/matrix math), Maneuver (impulsive burns), Spacecraft (craft logic), Config Validator (validation), Test Utilities (testing helpers), Renderer (3D visualization), UI Renderer (2D panels), and Config Loader (TOML parsing). Main depends on simulation, renderer, and UI renderer. Simulation module coordinates physics calculations and depends on orbital mechanics, maneuver, spacecraft, and config loader modules. Renderer depends on config loader for configuration. See module sections below for detailed descriptions. ## Core Data Structures ### Vec3 (physics.h) +3D vector for position, velocity, and acceleration ```cpp struct Vec3 { double x, y, z; @@ -53,6 +17,7 @@ struct Vec3 { ``` ### Mat3 (physics.h) +3x3 row-major matrix for coordinate transformations ```cpp struct Mat3 { double m00, m01, m02; @@ -60,25 +25,37 @@ struct Mat3 { double m20, m21, m22; }; ``` -Row-major 3x3 matrix for 3D rotation operations. + +### OrbitalElements (orbital_mechanics.h) +Keplerian orbital elements using union for parabolic/hyperbolic distinction +```cpp +struct OrbitalElements { + union { + double semi_major_axis; // elliptical (e<1) and hyperbolic (e>1) + double semi_latus_rectum; // parabolic (e≈1) + }; + double eccentricity; + double true_anomaly; + double inclination; + double longitude_of_ascending_node; + double argument_of_periapsis; +}; +``` ### CelestialBody (simulation.h) +Planet/moon with local and global coordinate frames ```cpp struct CelestialBody { char name[64]; double mass; // kg double radius; // meters - int parent_index; // index of gravitational parent (-1 for root body like Sun) + int parent_index; // index of gravitational parent (-1 for root) float color[3]; // RGB color for rendering - // Orbital elements from config (Keplerian elements) - OrbitalElements orbit; - - // Global frame (from origin) - Vec3 global_position; // meters from origin - Vec3 global_velocity; // m/s + OrbitalElements orbit; // Keplerian elements from config - // Local frame (relative to parent) + Vec3 global_position; // meters from origin + Vec3 global_velocity; // m/s Vec3 local_position; // meters from parent Vec3 local_velocity; // m/s relative to parent @@ -86,80 +63,26 @@ struct CelestialBody { }; ``` -### SimulationState (simulation.h) -```cpp -struct SimulationState { - CelestialBody* bodies; - int body_count; - int max_bodies; - - Spacecraft* spacecraft; - int craft_count; - int max_craft; - - Maneuver* maneuvers; - int maneuver_count; - int max_maneuvers; - - double time; // simulation time (seconds) - double dt; // time step (seconds) - char config_name[256]; -}; -``` - -### OrbitalElements (orbital_mechanics.h) -```cpp -struct OrbitalElements { - union { - double semi_major_axis; // for elliptical (e<1) and hyperbolic (e>1) - double semi_latus_rectum; // for parabolic (e≈1) - }; - double eccentricity; - double true_anomaly; - double inclination; - double longitude_of_ascending_node; - double argument_of_periapsis; -}; -``` -**Note:** 3D orientation is fully implemented using rotation matrices. The rotation sequence is z-x-z Euler angles: R_z(Ω) · R_x(i) · R_z(ω). All 3D parameters are applied in `orbital_elements_to_cartesian()`. - ### Spacecraft (spacecraft.h) +Spacecraft similar to CelestialBody but without SOI or radius ```cpp struct Spacecraft { char name[64]; double mass; int parent_index; - // Orbital elements from config OrbitalElements orbit; - // Global frame (from origin) Vec3 global_position; Vec3 global_velocity; - - // Local frame (relative to parent) Vec3 local_position; Vec3 local_velocity; }; ``` ### Maneuver (maneuver.h) +Impulsive burn with execution trigger ```cpp -enum BurnDirection { - BURN_PROGRADE, - BURN_RETROGRADE, - BURN_NORMAL, - BURN_ANTINORMAL, - BURN_RADIAL_IN, - BURN_RADIAL_OUT, - BURN_CUSTOM -}; - -enum TriggerType { - TRIGGER_TIME, - TRIGGER_TRUE_ANOMALY -}; - struct Maneuver { char name[64]; int craft_index; @@ -172,384 +95,463 @@ struct Maneuver { }; ``` -### OrbitalMetrics (test_utilities.h) +### SimulationState (simulation.h) +Top-level simulation container ```cpp -struct OrbitalMetrics { - double kinetic_energy; - double potential_energy; - double total_energy; - double orbital_radius; - double velocity_magnitude; - double angular_position; -}; -``` +struct SimulationState { + CelestialBody* bodies; + int body_count; + int max_bodies; -### OrbitTracker (test_utilities.h) -```cpp -struct OrbitTracker { - double initial_angle; - double previous_angle; - int quadrant_transitions; - bool orbit_completed; - double time_at_completion; - int body_index; - double min_time_days; - - // Orbital elements for 3D angle calculation - double inclination; - double longitude_of_ascending_node; - double argument_of_periapsis; - bool has_orbital_elements; + Spacecraft* spacecraft; + int craft_count; + int max_craft; + + Maneuver* maneuvers; + int maneuver_count; + int max_maneuvers; + + double time; + double dt; + char config_name[256]; }; ``` -**3D Support:** When `has_orbital_elements` is true, the tracker transforms 3D positions back to the orbital plane using the stored orbital elements before calculating the angle. This enables accurate period measurement for inclined orbits. - -## Module Overview - -### Physics (physics.cpp/h) -Vector math and gravity calculations. RK4 (Runge-Kutta 4th order) integration with `rk4_step()`. Physics module is independent of simulation structures and accepts direct parameters for improved modularity. - -**Key functions:** -- `rk4_step(Vec3* position, Vec3* velocity, double dt, double body_mass, double parent_mass)` - RK4 integration using position/velocity pointers -- `evaluate_acceleration(Vec3 relative_pos, double body_mass, double parent_mass)` - computes gravitational acceleration from parent -- Matrix operations for 3D orbital orientation: - - `mat3_identity()` - returns identity matrix - - `mat3_multiply(Mat3 a, Mat3 b)` - matrix-matrix multiplication - - `mat3_multiply_vec3(Mat3 m, Vec3 v)` - matrix-vector multiplication - - `mat3_rotation_x(double angle)` - rotation about X axis - - `mat3_rotation_z(double angle)` - rotation about Z axis - - `mat3_rotation_orbital(double omega, double i, double Omega)` - combined z-x-z orbital rotation - -**Implementation:** -- Row-major 3x3 matrix format (different from raylib's column-major 4x4) -- Rotation matrices follow standard right-hand rule convention -- Combined orbital rotation: R_z(Ω) · R_x(i) · R_z(ω) for z-x-z Euler angles - -### Orbital Mechanics (orbital_mechanics.cpp/h) -Keplerian orbital elements to Cartesian coordinate conversion. Supports all orbit types with full 3D orientation using rotation matrices. - -**Key functions:** -- `orbital_elements_to_cartesian(OrbitalElements elements, double parent_mass, Vec3* out_position, Vec3* out_velocity)` - converts Keplerian elements to local position/velocity with 3D orientation - -**Implementation:** -- Circular orbits (e=0): Position on circle, velocity from vis-viva equation -- Elliptical orbits (01): Same as elliptical with negative semi_major_axis -- All elements use SI units (meters, radians) -- `true_anomaly = 0` is at periapsis (closest approach) -- 3D orientation: applies z-x-z Euler rotations (R_z(Ω) · R_x(i) · R_z(ω)) to position and velocity vectors - -### Simulation (simulation.cpp/h) -Simulation state management and updates. SOI detection using Hill sphere: `r_soi = a * (m/M)^(2/5)`. - -**Key functions:** -- `find_dominant_body()` - determines which body has gravitational dominance -- `update_soi()` - calculates sphere of influence radius using Hill sphere -- `update_simulation()` - runs one physics step: finds dominant parent, calculates gravity, applies RK4 integration -- `initialize_local_coordinates()` - converts global to local coordinates on load -- `compute_global_coordinates()` - converts local to global coordinates after update -- Dynamic parent switching when bodies cross SOI boundaries (with hysteresis) - -**Update Order:** -- Root bodies updated first (in their own frame = global) -- Global coordinates computed for roots -- Child bodies updated in parent's local frame -- Global coordinates computed for all children - -**Additional functions:** -- `create_simulation()` / `destroy_simulation()` - memory management for simulation state -- `add_body_to_simulation()` / `add_spacecraft()` - dynamic addition of bodies and spacecraft -- `update_bodies_physics()` / `update_spacecraft_physics()` - separated physics updates -- `execute_pending_maneuvers()` - processes scheduled burn maneuvers -- `compute_spacecraft_globals()` - calculates global coordinates for all spacecraft -- `initialize_bodies()` - combined initialization for velocities, SOI radii, and local coordinates - -### Maneuver (maneuver.cpp/h) -Burn execution system for spacecraft orbital maneuvers. Supports multiple burn directions and trigger types. - -**Enums:** -- `BurnDirection`: PROGRADE, RETROGRADE, NORMAL, ANTINORMAL, RADIAL_IN, RADIAL_OUT, CUSTOM -- `TriggerType`: TIME, TRUE_ANOMALY - -**Key functions:** -- Direction calculation: `calculate_prograde_dir()`, `calculate_retrograde_dir()`, `calculate_normal_dir()`, `calculate_antinormal_dir()`, `calculate_radial_in_dir()`, `calculate_radial_out_dir()`, `get_burn_direction_vector()` -- Burn application: `apply_impulsive_burn()`, `apply_custom_burn()` -- Execution: `check_maneuver_trigger()`, `execute_maneuver()` -- Utility: `calculate_orbital_velocity()` - -**Implementation:** -- All burn direction vectors calculated in local frame relative to current orbital state -- Impulsive burns apply instantaneous velocity changes -- Trigger types allow time-based or orbital-position-based burn execution -- Maneuvers track execution state and timestamp - -### Config Loader (config_loader.cpp/h) -TOML-based config parser using tomlc17 library. Auto-calculates circular orbit velocities from orbital elements and SOI radii. - -**Key functions:** -- `load_system_config()` - main entry point, loads bodies/spacecraft/maneuvers from config file -- `parse_toml_body()` - parses individual body entries -- `parse_toml_spacecraft()` - parses spacecraft entries -- `parse_toml_maneuver()` - parses maneuver entries -- `load_spacecraft_from_toml()` - loads spacecraft array from config -- `load_maneuvers_from_toml()` - loads maneuvers array from config - -**Config format details:** -- TOML arrays: `[[bodies]]`, `[[spacecraft]]`, `[[maneuvers]]` -- Comments start with `#` -- `parent_index = -1` indicates root body (star) -- Supports nested orbits (planets with moons) -- All numeric values accept integers or floats -- Uses orbital elements table (Keplerian elements) instead of state vectors - -**Config format (TOML) - Bodies:** -```toml -[[bodies]] -name = "Sun" -mass = 1.989e30 -radius = 6.96e8 -parent_index = -1 -color = { r = 1.0, g = 1.0, b = 0.0 } -orbit = { - semi_major_axis = 0.0, - eccentricity = 0.0, - true_anomaly = 0.0 -} - -[[bodies]] -name = "Earth" -mass = 5.972e24 -radius = 6.371e6 -parent_index = 0 -color = { r = 0.0, g = 0.5, b = 1.0 } -orbit = { - semi_major_axis = 1.496e11, - eccentricity = 0.0, - true_anomaly = 0.0 -} -``` -**Config format (TOML) - Spacecraft:** -```toml -[[spacecraft]] -name = "LEO_Satellite" -mass = 1000.0 -parent_index = 1 -orbit = { - semi_major_axis = 6.571e6, - eccentricity = 0.0, - true_anomaly = 0.0 -} -``` -- Uses `orbit` table for orbital elements (same as bodies) -- Optional 3D orbital elements (inclination, longitude_of_ascending_node, argument_of_periapsis) are defined but not yet applied (deferred) - -**Config format (TOML) - Maneuvers:** -```toml -[[maneuvers]] -name = "orbit_raise" -spacecraft_name = "LEO_Satellite" -trigger_type = "time" -trigger_value = 3600.0 -direction = "prograde" -delta_v = 500.0 -``` -- `trigger_type`: "time" (seconds) or "true_anomaly" (radians) -- `direction`: "prograde", "retrograde", "normal", "antinormal", "radial_in", "radial_out" -- `delta_v`: velocity change magnitude in m/s - -### Config Validator (config_validator.cpp/h) -Validation module that ensures config files define physically realistic systems. Called automatically after loading config and initializing orbital objects. - -**Key functions:** -- `run_all_config_validations()` - master validation function that calls all validators -- `validate_parent_index_ordering()` - ensures parent indices are properly ordered -- `validate_orbital_elements()` - checks orbital elements for valid values -- `validate_initial_positions()` - ensures bodies aren't inside their parent -- `validate_mass_ratios()` - checks parent-child mass ratios for hierarchical consistency -- `validate_soi_overlap()` - detects bodies with overlapping spheres of influence -- `validate_nested_orbits()` - ensures moons orbit within stable boundaries - -**Validation rules:** -- Body count must not exceed `max_bodies` -- Spacecraft count must not exceed `max_craft` -- Maneuver count must not exceed `max_maneuvers` -- Body `parent_index` must be < body index or -1 (root) -- Spacecraft `parent_index` must reference valid body -- Maneuver `spacecraft_name` must reference existing spacecraft -- Maneuver names must be unique within config -- Eccentricity must be >= 0 -- Parabolic orbits (e≈1) require `semi_latus_rectum` (not `semi_major_axis`) -- Elliptical/hyperbolic orbits require `semi_major_axis` (not `semi_latus_rectum`) -- Elliptical orbits (e<1) require positive `semi_major_axis` -- Bodies with `eccentricity < 1` and `semi_major_axis <= 0` are rejected -- Parent-child distance must exceed combined radii -- Spacecraft must be at least parent's radius away -- **Mass ratio validation**: For root children with radius > 50% of parent, mass ratio >= 1000 -- **SOI overlap validation**: Bodies sharing same parent must not have overlapping SOIs -- **Nested orbit validation**: Moons (small radius, circular orbits) must orbit within 5x parent SOI - -### Renderer (renderer.cpp/h) -Raylib 3D visualization system. See **[docs/rendering.md](rendering.md)** for complete documentation including: -- Camera controls and follow system -- Object rendering (bodies, spacecraft, maneuvers) -- Orbit path rendering (elliptical, parabolic, hyperbolic) -- Coordinate transformation and scaling - -### UI Renderer (ui_renderer.cpp/h) -Raygui-based UI system for rendering interactive panels. Handles all 2D overlay elements. - -**Key functions:** -- `render_info()` - Bottom-left info panel showing simulation time, FPS, and controls -- `render_body_list_ui()` - Top-left objects list panel for selecting bodies/spacecraft -- `render_body_info_ui()` - Top-right panel showing selected object details -- `render_maneuver_list_ui()` - Maneuver list panel for spacecraft planning - -**UI State (UIState struct):** -- `body_list_scroll` - Scroll position for objects list -- `body_list_active` - Currently selected item index in body list -- `selected_craft_index` - Selected spacecraft index (-1 = no selection) - -**Implementation:** -- Uses raygui header-only library -- All UI rendering happens after 3D scene rendering -- Manages user interaction for object selection and camera following - -### Test Utilities (test_utilities.cpp/h) -Test helper functions for orbital mechanics validation. - -**Key functions:** -- `calculate_kinetic_energy()` - computes kinetic energy of a body -- `calculate_potential_energy_pair()` - computes gravitational potential energy between two bodies -- `calculate_system_total_energy()` - sums total energy of entire system -- `calculate_orbital_metrics()` - returns comprehensive orbital state metrics -- `create_orbit_tracker()` - initializes orbit completion tracking -- `update_orbit_tracker()` - tracks orbital progress and detects completion -- `compare_double()` / `compare_vec3()` - floating-point comparison with tolerance - -### Main Program (main.cpp) -GUI-only application with interactive 3D visualization. -- Initializes simulation with MAX_BODIES=100, TIME_STEP=60 seconds -- Runs 100 physics steps per frame (adjustable with speed multiplier) -- Game loop: input handling → camera update → physics update (if not paused) → rendering -- Supports speed multiplier (2x/0.5x per keypress, min 0.125x) -- Default config: `tests/configs/solar_system.toml` - -**Controls:** -- Arrow keys: Rotate and zoom camera -- Space: Pause/Resume -- +/-: Speed up/slow down simulation -- I: Toggle info display -- ESC: Quit - -## Build System - -### Makefile Targets -- `make` - Build raylib (first time) and compile sources to `orbit_sim` -- `make rebuild` - Clean and rebuild -- `make clean` - Remove build artifacts -- `make clean-all` - Clean everything including raylib -- `make run` - Build and run the simulation -- `make test` - Run full automated test suite -- `make test-build` - Build test executable - -### Dependencies -- g++ (C++14) -- raylib (built automatically from `ext/raylib/src`) -- tomlc17 (included in `ext/tomlc17/src`) -- Catch2 (for testing) -- libX11, libGL, libpthread (system libraries) - -### Test Infrastructure -- **Framework**: Catch2 for unit testing - - use `./orbit_test -s '[CONFIG_NAME]'` to show extra [INFO] messages on passing tests if needed -- **Test Configs**: `tests/configs/` contains test scenarios - - `solar_system.toml` - Full solar system with moons - - `earth_circular.toml`, `mars_circular.toml` - Simple orbital tests - - `parabolic_comet.toml` - Parabolic orbit (e=1.0) - - `hyperbolic_comet.toml` - Hyperbolic orbit (e>1.0) - - `soi_transition.toml` - SOI crossing test (3-body system) -- **Test Files**: - - `test_energy.cpp` - Energy conservation validation - - `test_moon_orbits.cpp` - Moon orbital stability tests - - `test_orbital_period.cpp` - Orbital period verification - - `test_parabolic_orbit.cpp` - Parabolic orbit tests - - `test_hyperbolic_orbit.cpp` - Hyperbolic orbit tests - - `test_soi_transition.cpp` - SOI transition validation +## Coordinate Frame System + +### Local Frame +- Position/velocity relative to parent body +- Used for orbital mechanics calculations +- Primary coordinate system for propagation + +### Global Frame +- Position/velocity from simulation origin (Sun at 0,0,0) +- Computed each frame: `global = parent.global + local` +- Used for SOI distance calculations and rendering + +### Benefits +- Precision: Local coordinates use full double precision for small orbits (LEO, moon) +- Clarity: Separates orbit physics from system-wide positions +- Efficiency: SOI checks use global distances, propagation uses local +- Flexibility: Easy to add/remove bodies by updating parent_index + +### Conversion Functions +- Local to global: `vec3_add(parent.global, local)` +- Global to local: `vec3_sub(global, parent.global)` + +## Key Modules + +### 1. Physics Module (physics.h/cpp) +Vector and matrix math utilities for orbital mechanics. + +**Constants:** +- Gravitational constant: `G` in physics.h + +**Vector Functions:** +- `vec3_add`, `vec3_sub`, `vec3_cross`, `vec3_scale` +- `vec3_magnitude`, `vec3_distance`, `vec3_normalize`, `vec3_dot` + +**Matrix Functions:** +- `mat3_identity`, `mat3_multiply`, `mat3_multiply_vec3` +- `mat3_rotation_x`, `mat3_rotation_z` +- `mat3_rotation_orbital(omega, i, Omega)` - z-x-z Euler angles + +**Physics Functions:** +- `calculate_acceleration(force, mass)` +- `evaluate_acceleration(relative_pos, body_mass, parent_mass)` +- `rk4_step()` - Available but NOT used in simulation (analytical used instead) + +### 2. Orbital Mechanics Module (orbital_mechanics.h/cpp) +Keplerian orbit propagation and element conversion. + +**Constants:** +- Parabolic tolerance: `PARABOLIC_TOLERANCE` in orbital_mechanics.h +- Kepler solver tolerance: `KEPLER_TOLERANCE` in orbital_mechanics.cpp + +**Element Conversion:** +- `orbital_elements_to_cartesian(elements, parent_mass, out_pos, out_vel)` (orbital_mechanics.cpp:6-44) + - Converts Keplerian elements to position/velocity vectors + - Uses z-x-z Euler rotation: R_z(Ω) · R_x(i) · R_z(ω) + +- `cartesian_to_orbital_elements(position, velocity, parent_mass)` (orbital_mechanics.cpp:186-299) + - Reconstructs elements from state vectors + - Handles near-parabolic (|ε| < 1e-10), near-circular (e < 1e-10) cases + - Returns semi_latus_rectum for parabolic orbits + +**Kepler Equation Solvers:** +- `solve_kepler_elliptical(mean_anomaly, eccentricity)` + - Newton-Raphson: E - e·sin(E) = M + +- `solve_kepler_hyperbolic(mean_anomaly, eccentricity)` + - Newton-Raphson: H - e·sinh(H) = M + +- `solve_barker_equation(mean_anomaly)` + - Cubic solution: D + D³/3 = M where D = tan(ν/2) + +**Anomaly Conversions:** +- `mean_anomaly_to_true_anomaly(mean_anomaly, eccentricity)` + - Unified dispatcher for elliptical/hyperbolic + +- `eccentric_to_true_anomaly(eccentric_anomaly, eccentricity)` + - Tangent half-angle formula: tan(ν/2) = √((1+e)/(1-e)) · tan(E/2) + - Near-parabolic special case for stability + +- `hyperbolic_to_true_anomaly(hyperbolic_anomaly, eccentricity)` + - tan(ν/2) = √((e+1)/(e-1)) · tanh(H/2) + +**Primary Propagation Function:** +- `propagate_orbital_elements(elements, dt, parent_mass)` (orbital_mechanics.cpp:301-375) + - **Critical: Main propagation method used by simulation** + - Dispatches to parabolic/elliptical/hyperbolic based on eccentricity + - Parabolic: Uses Barker's equation with mean motion n = √(μ/p³) + - Elliptical: Kepler's equation with n = √(μ/a³) + - Hyperbolic: Hyperbolic Kepler equation with n = √(μ/(-a)³) + +### 3. Simulation Module (simulation.h/cpp) +Main simulation loop, SOI management, coordinate updates. + +**SOI Calculation:** +- `calculate_soi_radius(body, parent)` + - Hill sphere: r_soi = a · (m/M)^(2/5) + +- `update_soi(body, parent, semi_major_axis)` + - Updates body->soi_radius, sets 1e15 for root bodies + +**SOI Transition Logic:** +- `find_dominant_body(sim, body_index)` (simulation.cpp:105-148) + - For non-root bodies: checks if still within parent's SOI + - For root bodies: finds closest body within SOI + - Returns new parent index or 0 (Sun) + +**Main Update Loop:** +- `update_simulation(sim)` + - Sequence: bodies_physics → global_coords → spacecraft_physics → maneuvers → spacecraft_globals → time++ + +**Body Physics:** +- `update_bodies_physics(sim)` + - Checks for SOI transitions each frame + - On transition: reconstructs orbital elements for new parent + - **Velocity deviation detection:** rebuilds elements if |v_local - v_expected| > 1e-6 (simulation.cpp:267-270) + - Propagates using `propagate_orbital_elements()` + - Updates local position/velocity from new elements + +**Spacecraft Physics:** +- `update_spacecraft_physics(sim)` + - Same velocity deviation detection as bodies (simulation.cpp:291-294) + - Propagates and updates coordinates + +**Coordinate Management:** +- `compute_global_coordinates(sim)` + - Updates global positions from parent.global + local + +- `compute_spacecraft_globals(sim)` + - Same for spacecraft + +**Initialization:** +- `initialize_orbital_objects(sim)` + - Converts orbital elements to local coordinates for all bodies/craft + - Calculates SOI radii for all bodies + +**Dynamic Management:** +- `create_simulation(max_bodies, max_craft, max_maneuvers, dt)` +- `destroy_simulation(sim)` +- `add_body_to_simulation(sim, body)` +- `add_spacecraft(sim, craft)` + +### 4. Maneuver Module (maneuver.h/cpp) +Impulsive burn execution with various burn directions. + +**Burn Directions:** +- `BURN_PROGRADE` - Along velocity vector +- `BURN_RETROGRADE` - Opposite velocity +- `BURN_NORMAL` - Along angular momentum (orbit normal) +- `BURN_ANTINORMAL` - Opposite angular momentum +- `BURN_RADIAL_IN` - Toward parent +- `BURN_RADIAL_OUT` - Away from parent +- `BURN_CUSTOM` - User-specified vector + +**Trigger Types:** +- `TRIGGER_TIME` - Execute at simulation time >= trigger_value +- `TRIGGER_TRUE_ANOMALY` - Execute when true anomaly within 0.01 rad of trigger_value + +**Direction Calculation:** +- `get_burn_direction_vector(direction, local_pos, local_vel)` + - Dispatches to specific direction calculators + +**Burn Application:** +- `apply_impulsive_burn(craft, direction, delta_v)` + - Instant velocity change: v_new = v_old + direction_vector · delta_v + +- `apply_custom_burn(craft, delta_v_local)` + - Applies arbitrary delta-v vector + +**Trigger Checking:** +- `check_maneuver_trigger(maneuver, craft, sim)` + - Time trigger: sim->time >= trigger_value + - True anomaly trigger: computes ν from state, checks angular distance < 0.01 rad + +**Execution:** +- `execute_maneuver(maneuver, craft, sim, current_time)` (maneuver.cpp:166-176) + - Applies burn + - **Reconstructs orbital elements** from new velocity + - Marks executed, records time + +### 5. Config Loader Module (config_loader.h/cpp) +TOML parsing for simulation configuration. + +**Main Function:** +- `load_system_config(sim, filepath)` - Parses TOML file, loads bodies, spacecraft, maneuvers + +**Config Format:** +- TOML format with [[bodies]], [[spacecraft]], [[maneuvers]] arrays +- See `docs/config_format.md` for complete documentation including all fields, validation rules, and examples + +### 6. Config Validator Module (config_validator.h/cpp) +System-level validation to prevent simulation errors. + +**Validation Functions:** +- `run_all_config_validations(sim)` - Master validation function +- See `docs/config_format.md` for all validation rules and constants + +### 7. Renderer Module (renderer.h/cpp) +3D visualization using Raylib. See `docs/rendering.md` for detailed reference. + +**Key Features:** +- Simulation XY plane → Render XZ plane transformation (90° rotation around X-axis) +- Linear distance/size scaling (1e-9 factor: 1 render unit = 1 billion meters) +- Relative rendering when body selected (children rendered around origin) +- Orbit path rendering (elliptical, parabolic, hyperbolic with appropriate segment counts) +- Wireframe spheres for bodies +- Child indicators for spacecraft/off-screen bodies +- Camera follow mode with orbit controls + +### 8. UI Renderer Module (ui_renderer.h/cpp) +2D UI panels using raygui. + +**Panels:** +- Info panel (bottom-left): Sim time, body count, FPS, controls, config name +- Objects list (top-left): All bodies and spacecraft with selection +- Info panel (top-right): Selected object details +- Maneuver list (below info panel): Pending/executed maneuvers + +### 9. Test Utilities Module (test_utilities.h/cpp) +Energy calculations and orbit tracking for testing. + +**OrbitalMetrics:** +- kinetic_energy, potential_energy, total_energy +- orbital_radius, velocity_magnitude +- angular_position + +**OrbitTracker:** +- Tracks orbit completion with quadrant transitions +- 3D angle calculation using orbital elements +- `create_orbit_tracker_3d()` for inclined orbits + +**Energy Functions:** +- `calculate_kinetic_energy(body)` +- `calculate_potential_energy_pair(body1, body2)` +- `calculate_system_total_energy(sim)` + +**Orbit Tracking:** +- `create_orbit_tracker(body_index)` +- `update_orbit_tracker(tracker, body, parent, current_time)` +- `orbit_completed` flag, `time_at_completion` record + +## Critical Implementation Details + +### Analytical Propagation Algorithm + +**Function:** `propagate_orbital_elements(elements, dt, parent_mass)` (orbital_mechanics.cpp:301-375) + +**Elliptical Orbits (e < 1):** +1. Compute mean motion: n = √(μ/a³) +2. Convert true anomaly to eccentric anomaly: E = 2·atan(√((1-e)/(1+e)) · tan(ν/2)) +3. Compute mean anomaly: M = E - e·sin(E) +4. Advance mean anomaly: M_new = M + n·dt +5. Solve Kepler equation for E_new using Newton-Raphson (max 50 iterations, tolerance 1e-10) +6. Convert back to true anomaly: ν_new = 2·atan(√((1+e)/(1-e)) · tan(E_new/2)) +7. Return elements with updated true_anomaly + +**Parabolic Orbits (|e-1| < PARABOLIC_TOLERANCE):** +1. Compute variable: D = tan(ν/2) +2. Compute Barker's mean anomaly: M = D + D³/3 +3. Compute mean motion: n = √(μ/p³) where p = semi_latus_rectum +4. Advance: M_new = M + n·dt +5. Solve Barker's equation: D_new from cubic formula +6. Convert: ν_new = 2·atan(D_new) +7. Return elements with updated true_anomaly + +**Hyperbolic Orbits (e > 1):** +1. Compute mean motion: n = √(μ/(-a)³) (note negative a) +2. Convert true anomaly to hyperbolic anomaly: H = 2·atanh(√((e-1)/(e+1)) · tan(ν/2)) +3. Compute mean anomaly: M = e·sinh(H) - H +4. Advance: M_new = M + n·dt +5. Solve hyperbolic Kepler equation for H_new using Newton-Raphson (max 50 iterations, tolerance 1e-10) +6. Convert back: ν_new = 2·atan(√((e+1)/(e-1)) · tanh(H_new/2)) +7. Return elements with updated true_anomaly + +**Key Benefits:** +- Exact solution to Kepler's laws (no integration error) +- Energy conservation guaranteed +- Efficient for large time steps +- Handles all orbit types uniformly + +### SOI Transition Logic + +**Function:** `find_dominant_body(sim, body_index)` (simulation.cpp:105-148) + +**Algorithm:** +1. If parent is not root (index != 0): + - Check if distance to parent < parent.soi_radius + - If yes: stay with current parent + - If no: transition to root (Sun) + +2. If parent is root (Sun): + - Iterate through all other bodies + - Find closest body where distance < body.soi_radius + - Switch to that body (if found, else stay with root) + +**Transition Handling:** +1. When `find_dominant_body()` returns new parent: + - Compute current global position/velocity from old parent + - Update `body->parent_index` + - Compute new local coordinates relative to new parent + - **Reconstruct orbital elements:** `cartesian_to_orbital_elements()` + +**Orbital Element Reconstruction:** +- Converts local position/velocity to new Keplerian elements +- Handles edge cases (near-circular, near-parabolic) +- Ensures subsequent propagation is consistent with new frame + +### Orbital Element Reconstruction After Burns + +**Location:** `execute_maneuver()` (maneuver.cpp:166-176) + +**Process:** +1. Apply impulsive burn to `craft->local_velocity` +2. **Reconstruct elements:** `cartesian_to_orbital_elements(local_pos, local_vel, parent_mass)` +3. New elements are used for subsequent analytical propagation + +**Velocity Deviation Detection:** +- Before each propagation, check: `|v_local - v_expected_from_elements| > 1e-6` (simulation.cpp:267-270, 291-294) +- If exceeded: reconstruct elements from current state +- This catches numerical drift and ensures consistency + +### 3D Orbital Orientation + +**Rotation:** z-x-z Euler angles via `mat3_rotation_orbital(omega, i, Omega)` + +**Sequence:** +1. Rotate by argument_of_periapsis (ω) around Z-axis +2. Rotate by inclination (i) around X-axis +3. Rotate by longitude_of_ascending_node (Ω) around Z-axis + +**Matrix:** R_total = R_z(Ω) · R_x(i) · R_z(ω) + +**Purpose:** +- Orient orbital plane in 3D space +- Align periapsis direction with eccentricity vector +- Define ascending node location + +**Element Conversion:** +- `orbital_elements_to_cartesian()` applies rotation +- `cartesian_to_orbital_elements()` computes ω, i, Ω from angular momentum and eccentricity vectors ## Orbit Types -### Elliptical Orbits (0 ≤ e < 1) -- Standard planetary and moon orbits -- Eccentricity e = 0 (circular) to e < 1 (elliptical) -- Total energy is negative (bound to parent) -- Velocity follows vis-viva equation: `v² = GM(2/r - 1/a)` +### Elliptical Orbits (e < 1) +- **Energy:** ε < 0 (negative specific orbital energy) +- **Semi-major axis:** a = -μ/(2ε) (positive) +- **Motion:** Bound, periodic +- **Propagation:** Uses elliptical Kepler equation: E - e·sin(E) = M +- **Examples:** Planets, moons, satellites -### Parabolic Orbits (e = 1) -- Escape trajectories with exactly escape velocity -- Total energy is zero (marginally unbound) -- Escape velocity: `v² = 2GM/r` -- Rendered using true anomaly range: -π*0.95 to π*0.95 -- Used for comets on escape trajectories +### Parabolic Orbits (|e-1| < PARABOLIC_TOLERANCE) +- **Energy:** ε ≈ 0 (zero specific orbital energy, escape velocity) +- **Parameter:** Semi-latus rectum p = h²/μ +- **Motion:** Escape trajectory, asymptotic velocity → 0 at infinity +- **Propagation:** Uses Barker's equation: D + D³/3 = M where D = tan(ν/2) +- **Examples:** Escape missions, comet-like trajectories ### Hyperbolic Orbits (e > 1) -- Fast escape trajectories exceeding escape velocity -- Total energy is positive (unbound) -- Asymptotic velocity: `v∞ = √(2GM/|a|)` where a < 0 -- Shows open curve with asymptotic behavior -- Used for high-speed comets and interstellar objects +- **Energy:** ε > 0 (positive specific orbital energy) +- **Semi-major axis:** a = -μ/(2ε) (negative) +- **Motion:** Unbound, excess velocity at infinity +- **Propagation:** Uses hyperbolic Kepler equation: H - e·sinh(H) = M +- **Examples:** Interplanetary trajectories, gravity assists + +**Velocity Notes:** +- Elliptical: v < v_escape at periapsis +- Parabolic: v = v_escape at all points +- Hyperbolic: v > v_escape at all points +- All satisfy vis-viva equation: v² = μ(2/r - 1/a) ## Data Flow ### Initialization Sequence -1. Configuration file → `load_system_config()` → populates `SimulationState`: - - Loads bodies array from `[[bodies]]` - - Loads spacecraft array from `[[spacecraft]]` - - Loads maneuvers array from `[[maneuvers]]` -2. `initialize_orbital_objects()` → combined initialization: - - Converts orbital elements to local position/velocity for all bodies - - Calculates circular orbit velocities for all bodies (using vis-viva equation) - - Computes sphere of influence radius for each body (Hill sphere) - - Sets local coordinates (position/velocity) for all bodies and spacecraft - - Initializes spacecraft global coordinates -3. `run_all_config_validations()` → validates loaded configuration: - - Checks parent_index ordering - - Validates orbital elements - - Checks mass ratios between parent and child bodies - - Detects overlapping SOIs between bodies sharing same parent - - Validates nested orbit boundaries (moons within parent's SOI) - - Ensures initial positions don't collide with parent body +The simulation initializes in this sequence: create_simulation() is called first, then load_system_config() parses the TOML file and loads bodies, spacecraft, and maneuvers. Next, run_all_config_validations() performs system-level validation. Then initialize_orbital_objects() converts orbital elements to local position/velocity for all bodies and spacecraft, computes global coordinates, and calculates SOI radii. Finally, the main simulation loop begins. ### Main Simulation Loop -1. `update_simulation()` → for each body: - - `find_dominant_body()` → determine gravitational parent based on SOI - - `evaluate_acceleration()` → compute gravitational force from parent - - `rk4_step()` → update position/velocity using Runge-Kutta 4th order -2. `render_simulation()` → for each body: - - `scale_position()` → convert to render coordinates using logarithmic scaling - - `scale_radius()` → convert to render size using exponential scaling - -### SOI Transition Mechanics -- Bodies dynamically switch gravitational parents when crossing SOI boundaries -- Uses 0.5x distance hysteresis to prevent oscillation between parents -- `find_dominant_body()` checks all bodies and selects most dominant influence - -## Technical Notes - -### Code Style and Architecture -- C-style C++: structs and functions only, no classes or templates -- All headers use include guards -- Memory management uses malloc/free -- Layer separation: Physics, Simulation, Configuration, Rendering layers -- Physics module is independent of simulation structures (parameter-based signatures) - -### Physics Considerations -- Timestep: 60 seconds for solar system scale -- Circular orbit velocity: `v = sqrt(G * M / r)` -- Physics steps per frame: 100 (default) with speed multiplier adjustment -- Simulation time per frame: 60s * 100 = 6000 seconds at 1x speed -- SOI (Sphere of Influence) uses Hill sphere approximation: `r_soi = a * (m/M)^(2/5)` -- SOI transitions use 0.5x distance hysteresis to prevent oscillation -- Parabolic orbits use escape velocity: `v² = 2GM/r` -- Hyperbolic orbits have positive total energy and asymptotic velocity +The main simulation loop executes in this order: update_bodies_physics(), compute_global_coordinates(), update_spacecraft_physics(), execute_pending_maneuvers(), compute_spacecraft_globals(), then increments simulation time. Within update_bodies_physics(), for each body: check SOI via find_dominant_body, handle transitions by computing global coordinates from old parent, updating parent_index, computing new local coordinates, and reconstructing orbital elements. Then check velocity deviation with 1e-6 tolerance and reconstruct elements if needed. Propagate elements via propagate_orbital_elements() and update local position/velocity. compute_global_coordinates() updates all body global positions from parent.global + local. update_spacecraft_physics() performs the same velocity deviation check and propagation for spacecraft. execute_pending_maneuvers() checks each unexecuted maneuver for time or true anomaly triggers; if triggered, applies burn to local_velocity, reconstructs orbital elements, and marks executed. compute_spacecraft_globals() updates all spacecraft global positions. + +### SOI Mechanics +SOI transitions are detected by calling find_dominant_body() before each physics update. If the parent changes, the body's global coordinates are computed in the old frame, the parent_index is updated, new local coordinates are computed, and orbital elements are reconstructed. Propagation then uses the new local frame. + +## Location Hints + +### Constants +- Gravitational constant: `G` in physics.h +- Parabolic tolerance: `PARABOLIC_TOLERANCE` in orbital_mechanics.h +- Kepler solver tolerance: `KEPLER_TOLERANCE` in orbital_mechanics.cpp +- SOI mass ratio: `MIN_MASS_RATIO` in config_validator.h + +### Config Files +- Example: `configs/solar_system.toml` +- Format: TOML with [[bodies]], [[spacecraft]], [[maneuvers]] arrays + +### Key Function Line Numbers +- `propagate_orbital_elements()`: orbital_mechanics.cpp:301-375 +- `find_dominant_body()`: simulation.cpp:105-148 +- `cartesian_to_orbital_elements()`: orbital_mechanics.cpp:186-299 +- `orbital_elements_to_cartesian()`: orbital_mechanics.cpp:6-44 +- `execute_maneuver()`: maneuver.cpp:166-176 +- Velocity deviation check: simulation.cpp:267-270 (bodies), 291-294 (spacecraft) + +### Build System +**Makefile targets:** +- `make` or `make all` - Build simulation +- `make raylib` - Build raylib dependency +- `make run` - Build and run simulation +- `make test` - Build and run automated tests +- `make test-build` - Build test executable only +- `make clean` - Remove build artifacts +- `make rebuild` - Clean and rebuild + +**Dependencies:** +- g++ (C++14 standard) +- raylib (git submodule in ext/raylib/) +- raygui (header-only, in ext/raygui/) +- tomlc17 (C TOML parser, in ext/tomlc17/) +- Catch2 (test framework, for test builds only) + +**Build output:** +- `orbit_sim` - Main simulation executable +- `orbit_test` - Test suite executable +- Object files in `build/` directory + +**Testing:** +```bash +# Run all tests +make test + +# Run specific test config +./orbit_test '[config_name]' + +# Test with extra debug output (shows INFO messages) +./orbit_test -s '[config_name]' +``` + +**Test framework:** +- Catch2 for testing +- Use `WithinAbs(expected, tolerance)` for floating-point comparisons (NOT `Approx()`) +- Required header: ``