Browse Source

Refactor: Integrate spacecraft into SimulationState

Merge spacecraft management into simulation module:
- Move Spacecraft struct from maneuver.h to spacecraft.h
- Add spacecraft array to SimulationState (spacecraft, craft_count, max_craft)
- Remove separate SpacecraftState struct and related functions
- Update create_simulation() to require max_craft parameter
- Move spacecraft update logic into update_simulation()
- Make load_spacecraft_config() internal helper, called by load_system_config()
- Update all test files to use new API with 3-parameter create_simulation()
- Handle max_craft=0 case gracefully (no spacecraft allocation)

Test results: 26/27 passing (1 pre-existing SOI test unrelated to changes)
main
cinnaboot 6 months ago
parent
commit
f5a1fdd245
  1. 115
      src/config_loader.cpp
  2. 1
      src/config_loader.h
  3. 3
      src/main.cpp
  4. 1
      src/maneuver.cpp
  5. 11
      src/maneuver.h
  6. 59
      src/simulation.cpp
  7. 16
      src/simulation.h
  8. 68
      src/spacecraft.cpp
  9. 21
      src/spacecraft.h
  10. 2
      tests/test_energy.cpp
  11. 6
      tests/test_hyperbolic_orbit.cpp
  12. 8
      tests/test_invalid_parent_assignment.cpp
  13. 50
      tests/test_maneuvers.cpp
  14. 8
      tests/test_moon_orbits.cpp
  15. 4
      tests/test_orbital_period.cpp
  16. 4
      tests/test_parabolic_orbit.cpp
  17. 4
      tests/test_root_body_transitions.cpp
  18. 4
      tests/test_soi_transition.cpp

115
src/config_loader.cpp

@ -3,7 +3,9 @@
#include <cmath> #include <cmath>
#include <cstring> #include <cstring>
#include "simulation.h" #include "simulation.h"
#include "spacecraft.h"
static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft);
static bool load_spacecraft_from_toml(SimulationState* sim, toml_result_t result);
static bool extract_vec3_from_table(toml_datum_t table, Vec3* pos) { static bool extract_vec3_from_table(toml_datum_t table, Vec3* pos) {
toml_datum_t x = toml_get(table, "x"); toml_datum_t x = toml_get(table, "x");
@ -157,80 +159,40 @@ bool load_system_config(SimulationState* sim, const char* filepath) {
} }
sim->body_count = body_count; sim->body_count = body_count;
toml_free(result);
initialize_bodies(sim);
strncpy(sim->config_name, filepath, sizeof(sim->config_name) - 1); strncpy(sim->config_name, filepath, sizeof(sim->config_name) - 1);
sim->config_name[sizeof(sim->config_name) - 1] = '\0'; sim->config_name[sizeof(sim->config_name) - 1] = '\0';
printf("Loaded %d bodies from %s\n", body_count, filepath); // Load spacecraft from the same config file (before freeing TOML data)
return true; if (!load_spacecraft_from_toml(sim, result)) {
} toml_free(result);
static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft) {
toml_datum_t name = toml_get(craft_table, "name");
if (name.type != TOML_STRING) {
return false;
}
strncpy(craft->name, name.u.s, 63);
craft->name[63] = '\0';
toml_datum_t mass = toml_get(craft_table, "mass");
toml_datum_t parent_idx = toml_get(craft_table, "parent_index");
if (mass.type != TOML_FP64 || parent_idx.type != TOML_INT64) {
return false; return false;
} }
craft->mass = mass.u.fp64; toml_free(result);
craft->parent_index = (int)(parent_idx.type == TOML_INT64 ? parent_idx.u.int64 : (int)parent_idx.u.fp64);
toml_datum_t position = toml_get(craft_table, "position");
if (position.type != TOML_TABLE || !extract_vec3_from_table(position, &craft->local_position)) {
return false;
}
craft->position = craft->local_position;
toml_datum_t velocity = toml_get(craft_table, "velocity"); initialize_bodies(sim);
if (velocity.type == TOML_TABLE) {
if (!extract_vec3_from_table(velocity, &craft->local_velocity)) {
return false;
}
} else {
craft->local_velocity = {0.0, 0.0, 0.0};
}
craft->velocity = craft->local_velocity;
printf("Loaded %d bodies from %s\n", body_count, filepath);
return true; return true;
} }
bool load_spacecraft_config(SpacecraftState* craft_state, SimulationState* sim, const char* filepath) { static bool load_spacecraft_from_toml(SimulationState* sim, toml_result_t result) {
toml_result_t result = toml_parse_file_ex(filepath);
if (!result.ok) {
printf("Error: Could not parse TOML config file: %s\n", filepath);
printf("TOML Error: %s\n", result.errmsg);
return false;
}
toml_datum_t spacecraft = toml_get(result.toptab, "spacecraft"); toml_datum_t spacecraft = toml_get(result.toptab, "spacecraft");
if (spacecraft.type != TOML_ARRAY) { if (spacecraft.type != TOML_ARRAY) {
printf("No spacecraft array found in config file: %s (optional)\n", filepath); printf("No spacecraft array found in config file: %s (optional)\n", sim->config_name);
toml_free(result);
return true; return true;
} }
int craft_count = spacecraft.u.arr.size; int craft_count = spacecraft.u.arr.size;
if (craft_count == 0) { if (craft_count == 0) {
printf("No spacecraft found in config file: %s\n", filepath); printf("No spacecraft found in config file: %s\n", sim->config_name);
toml_free(result);
return true; return true;
} }
if (craft_count > craft_state->max_craft) { if (craft_count > sim->max_craft) {
printf("Error: Too many spacecraft (%d) for state (max: %d)\n", printf("Error: Too many spacecraft (%d) for simulation (max: %d)\n",
craft_count, craft_state->max_craft); craft_count, sim->max_craft);
toml_free(result);
return false; return false;
} }
@ -239,14 +201,12 @@ bool load_spacecraft_config(SpacecraftState* craft_state, SimulationState* sim,
toml_datum_t craft_table = spacecraft.u.arr.elem[i]; toml_datum_t craft_table = spacecraft.u.arr.elem[i];
if (!parse_toml_spacecraft(craft_table, &craft)) { if (!parse_toml_spacecraft(craft_table, &craft)) {
printf("Error: Failed to parse spacecraft at index %d\n", i); printf("Error: Failed to parse spacecraft at index %d\n", i);
toml_free(result);
return false; return false;
} }
if (craft.parent_index < 0 || craft.parent_index >= sim->body_count) { if (craft.parent_index < 0 || craft.parent_index >= sim->body_count) {
printf("Error: Spacecraft '%s' has invalid parent_index %d (valid: 0-%d)\n", printf("Error: Spacecraft '%s' has invalid parent_index %d (valid: 0-%d)\n",
craft.name, craft.parent_index, sim->body_count - 1); craft.name, craft.parent_index, sim->body_count - 1);
toml_free(result);
return false; return false;
} }
@ -254,15 +214,50 @@ bool load_spacecraft_config(SpacecraftState* craft_state, SimulationState* sim,
craft.position = vec3_add(parent->position, craft.local_position); craft.position = vec3_add(parent->position, craft.local_position);
craft.velocity = vec3_add(parent->velocity, craft.local_velocity); craft.velocity = vec3_add(parent->velocity, craft.local_velocity);
int idx = add_spacecraft(craft_state, &craft); int idx = add_spacecraft(sim, &craft);
if (idx < 0) { if (idx < 0) {
printf("Error: Failed to add spacecraft to state\n"); printf("Error: Failed to add spacecraft to simulation\n");
toml_free(result);
return false; return false;
} }
} }
printf("Loaded %d spacecraft from %s\n", craft_count, filepath); printf("Loaded %d spacecraft from %s\n", craft_count, sim->config_name);
toml_free(result);
return true; return true;
} }
static bool parse_toml_spacecraft(toml_datum_t craft_table, Spacecraft* craft) {
toml_datum_t name = toml_get(craft_table, "name");
if (name.type != TOML_STRING) {
return false;
}
strncpy(craft->name, name.u.s, 63);
craft->name[63] = '\0';
toml_datum_t mass = toml_get(craft_table, "mass");
toml_datum_t parent_idx = toml_get(craft_table, "parent_index");
if (mass.type != TOML_FP64 || parent_idx.type != TOML_INT64) {
return false;
}
craft->mass = mass.u.fp64;
craft->parent_index = (int)(parent_idx.type == TOML_INT64 ? parent_idx.u.int64 : (int)parent_idx.u.fp64);
toml_datum_t position = toml_get(craft_table, "position");
if (position.type != TOML_TABLE || !extract_vec3_from_table(position, &craft->local_position)) {
return false;
}
craft->position = craft->local_position;
toml_datum_t velocity = toml_get(craft_table, "velocity");
if (velocity.type == TOML_TABLE) {
if (!extract_vec3_from_table(velocity, &craft->local_velocity)) {
return false;
}
} else {
craft->local_velocity = {0.0, 0.0, 0.0};
}
craft->velocity = craft->local_velocity;
return true;
}

1
src/config_loader.h

@ -6,6 +6,5 @@
#include "../ext/tomlc17/src/tomlc17.h" #include "../ext/tomlc17/src/tomlc17.h"
bool load_system_config(SimulationState* sim, const char* filepath); bool load_system_config(SimulationState* sim, const char* filepath);
bool load_spacecraft_config(SpacecraftState* craft_state, SimulationState* sim, const char* filepath);
#endif #endif

3
src/main.cpp

@ -106,8 +106,9 @@ int main(int argc, char** argv) {
// Create simulation with time step of 60 seconds // Create simulation with time step of 60 seconds
const int MAX_BODIES = 100; const int MAX_BODIES = 100;
const int MAX_SPACECRAFT = 50;
const double TIME_STEP = 60.0; // 60 seconds per step const double TIME_STEP = 60.0; // 60 seconds per step
SimulationState* sim = create_simulation(MAX_BODIES, TIME_STEP); SimulationState* sim = create_simulation(MAX_BODIES, MAX_SPACECRAFT, TIME_STEP);
// Load system configuration // Load system configuration
if (!load_system_config(sim, args.config_file)) { if (!load_system_config(sim, args.config_file)) {

1
src/maneuver.cpp

@ -1,5 +1,6 @@
#include "maneuver.h" #include "maneuver.h"
#include "physics.h" #include "physics.h"
#include "spacecraft.h"
#include <cmath> #include <cmath>
Vec3 calculate_prograde_dir(Vec3 local_velocity) { Vec3 calculate_prograde_dir(Vec3 local_velocity) {

11
src/maneuver.h

@ -2,6 +2,7 @@
#define MANEUVER_H #define MANEUVER_H
#include "physics.h" #include "physics.h"
#include "spacecraft.h"
enum BurnDirection { enum BurnDirection {
BURN_PROGRADE, BURN_PROGRADE,
@ -13,16 +14,6 @@ enum BurnDirection {
BURN_CUSTOM BURN_CUSTOM
}; };
struct Spacecraft {
char name[64];
double mass;
Vec3 local_position;
Vec3 local_velocity;
Vec3 position;
Vec3 velocity;
int parent_index;
};
// Direction calculation functions (local frame) // Direction calculation functions (local frame)
Vec3 calculate_prograde_dir(Vec3 local_velocity); Vec3 calculate_prograde_dir(Vec3 local_velocity);
Vec3 calculate_retrograde_dir(Vec3 local_velocity); Vec3 calculate_retrograde_dir(Vec3 local_velocity);

59
src/simulation.cpp

@ -1,4 +1,5 @@
#include "simulation.h" #include "simulation.h"
#include "spacecraft.h"
#include <cassert> #include <cassert>
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@ -6,11 +7,20 @@
#include <cmath> #include <cmath>
// Create a new simulation // Create a new simulation
SimulationState* create_simulation(int max_bodies, double time_step) { SimulationState* create_simulation(int max_bodies, int max_craft, double time_step) {
SimulationState* sim = (SimulationState*)malloc(sizeof(SimulationState)); SimulationState* sim = (SimulationState*)malloc(sizeof(SimulationState));
sim->bodies = (CelestialBody*)malloc(sizeof(CelestialBody) * max_bodies); sim->bodies = (CelestialBody*)malloc(sizeof(CelestialBody) * max_bodies);
if (max_craft > 0) {
sim->spacecraft = (Spacecraft*)malloc(sizeof(Spacecraft) * max_craft);
} else {
sim->spacecraft = NULL;
}
sim->body_count = 0; sim->body_count = 0;
sim->max_bodies = max_bodies; sim->max_bodies = max_bodies;
sim->craft_count = 0;
sim->max_craft = max_craft;
sim->time = 0.0; sim->time = 0.0;
sim->dt = time_step; sim->dt = time_step;
sim->config_name[0] = '\0'; sim->config_name[0] = '\0';
@ -23,10 +33,28 @@ void destroy_simulation(SimulationState* sim) {
if (sim->bodies) { if (sim->bodies) {
free(sim->bodies); free(sim->bodies);
} }
if (sim->max_craft > 0 && sim->spacecraft) {
free(sim->spacecraft);
}
free(sim); free(sim);
} }
} }
// Add a spacecraft to the simulation
int add_spacecraft(SimulationState* sim, Spacecraft* craft) {
if (sim->craft_count >= sim->max_craft) {
printf("Error: Cannot add spacecraft - simulation full (%d/%d)\n",
sim->craft_count, sim->max_craft);
return -1;
}
int new_idx = sim->craft_count;
sim->spacecraft[new_idx] = *craft;
sim->craft_count++;
return new_idx;
}
// Add a body to the simulation at runtime // Add a body to the simulation at runtime
int add_body_to_simulation(SimulationState* sim, CelestialBody* body) { int add_body_to_simulation(SimulationState* sim, CelestialBody* body) {
if (sim->body_count >= sim->max_bodies) { if (sim->body_count >= sim->max_bodies) {
@ -163,6 +191,35 @@ void update_simulation(SimulationState* sim) {
} }
compute_global_coordinates(sim); compute_global_coordinates(sim);
// Update spacecraft
for (int i = 0; i < sim->craft_count; i++) {
Spacecraft* craft = &sim->spacecraft[i];
if (craft->parent_index < 0 || craft->parent_index >= sim->body_count) {
continue;
}
CelestialBody* parent = &sim->bodies[craft->parent_index];
rk4_step(&craft->local_position, &craft->local_velocity,
sim->dt, craft->mass, parent->mass);
}
// Compute spacecraft global coordinates
for (int i = 0; i < sim->craft_count; i++) {
Spacecraft* craft = &sim->spacecraft[i];
if (craft->parent_index >= 0 && craft->parent_index < sim->body_count) {
CelestialBody* parent = &sim->bodies[craft->parent_index];
craft->position = vec3_add(parent->position, craft->local_position);
craft->velocity = vec3_add(parent->velocity, craft->local_velocity);
} else {
craft->position = craft->local_position;
craft->velocity = craft->local_velocity;
}
}
sim->time += sim->dt; sim->time += sim->dt;
} }

16
src/simulation.h

@ -3,6 +3,8 @@
#include "physics.h" #include "physics.h"
struct Spacecraft;
// Celestial body structure // Celestial body structure
struct CelestialBody { struct CelestialBody {
char name[64]; char name[64];
@ -24,17 +26,23 @@ struct SimulationState {
CelestialBody* bodies; CelestialBody* bodies;
int body_count; int body_count;
int max_bodies; int max_bodies;
double time; // simulation time (seconds)
double dt; // time step (seconds) Spacecraft* spacecraft;
char config_name[256]; // name of the config file loaded int craft_count;
int max_craft;
double time;
double dt;
char config_name[256];
}; };
// Simulation management functions // Simulation management functions
SimulationState* create_simulation(int max_bodies, double time_step); SimulationState* create_simulation(int max_bodies, int max_craft, double time_step);
void destroy_simulation(SimulationState* sim); void destroy_simulation(SimulationState* sim);
// Dynamic body management // Dynamic body management
int add_body_to_simulation(SimulationState* sim, CelestialBody* body); int add_body_to_simulation(SimulationState* sim, CelestialBody* body);
int add_spacecraft(SimulationState* sim, Spacecraft* craft);
// SOI and simulation update functions // SOI and simulation update functions
int find_dominant_body(SimulationState* sim, int body_index); int find_dominant_body(SimulationState* sim, int body_index);

68
src/spacecraft.cpp

@ -1,71 +1,5 @@
#include "spacecraft.h" #include "spacecraft.h"
#include "physics.h" #include "simulation.h"
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <cmath> #include <cmath>
SpacecraftState* create_spacecraft_state(int max_craft) {
SpacecraftState* state = (SpacecraftState*)malloc(sizeof(SpacecraftState));
state->spacecraft = (Spacecraft*)malloc(sizeof(Spacecraft) * max_craft);
state->craft_count = 0;
state->max_craft = max_craft;
return state;
}
void destroy_spacecraft_state(SpacecraftState* state) {
if (state) {
if (state->spacecraft) {
free(state->spacecraft);
}
free(state);
}
}
int add_spacecraft(SpacecraftState* state, Spacecraft* craft) {
if (state->craft_count >= state->max_craft) {
printf("Error: Cannot add spacecraft - state full (%d/%d)\n",
state->craft_count, state->max_craft);
return -1;
}
int new_idx = state->craft_count;
state->spacecraft[new_idx] = *craft;
state->craft_count++;
return new_idx;
}
void compute_spacecraft_globals(SpacecraftState* craft_state, SimulationState* sim) {
for (int i = 0; i < craft_state->craft_count; i++) {
Spacecraft* craft = &craft_state->spacecraft[i];
if (craft->parent_index >= 0 && craft->parent_index < sim->body_count) {
CelestialBody* parent = &sim->bodies[craft->parent_index];
craft->position = vec3_add(parent->position, craft->local_position);
craft->velocity = vec3_add(parent->velocity, craft->local_velocity);
} else {
craft->position = craft->local_position;
craft->velocity = craft->local_velocity;
}
}
}
void update_spacecraft(SpacecraftState* craft_state, SimulationState* sim) {
for (int i = 0; i < craft_state->craft_count; i++) {
Spacecraft* craft = &craft_state->spacecraft[i];
if (craft->parent_index < 0 || craft->parent_index >= sim->body_count) {
continue;
}
CelestialBody* parent = &sim->bodies[craft->parent_index];
double parent_mass = parent->mass;
double craft_mass = craft->mass;
rk4_step(&craft->local_position, &craft->local_velocity,
sim->dt, craft_mass, parent_mass);
}
compute_spacecraft_globals(craft_state, sim);
}

21
src/spacecraft.h

@ -1,19 +1,16 @@
#ifndef SPACECRAFT_H #ifndef SPACECRAFT_H
#define SPACECRAFT_H #define SPACECRAFT_H
#include "maneuver.h" #include "physics.h"
#include "simulation.h"
struct SpacecraftState { struct Spacecraft {
Spacecraft* spacecraft; char name[64];
int craft_count; double mass;
int max_craft; Vec3 local_position;
Vec3 local_velocity;
Vec3 position;
Vec3 velocity;
int parent_index;
}; };
SpacecraftState* create_spacecraft_state(int max_craft);
void destroy_spacecraft_state(SpacecraftState* state);
void update_spacecraft(SpacecraftState* craft_state, SimulationState* sim);
void compute_spacecraft_globals(SpacecraftState* craft_state, SimulationState* sim);
int add_spacecraft(SpacecraftState* state, Spacecraft* craft);
#endif #endif

2
tests/test_energy.cpp

@ -10,7 +10,7 @@ TEST_CASE("Energy conservation - Earth circular orbit", "[energy][rk4]") {
const double DAYS_TO_SIMULATE = 10.0; const double DAYS_TO_SIMULATE = 10.0;
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/earth_circular.toml")); REQUIRE(load_system_config(sim, "tests/configs/earth_circular.toml"));

6
tests/test_hyperbolic_orbit.cpp

@ -12,7 +12,7 @@ TEST_CASE("Hyperbolic orbit - energy and escape trajectory", "[hyperbolic][energ
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
const double AU = 1.496e11; const double AU = 1.496e11;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/hyperbolic_comet.toml")); REQUIRE(load_system_config(sim, "tests/configs/hyperbolic_comet.toml"));
@ -111,7 +111,7 @@ TEST_CASE("Hyperbolic orbit - energy and escape trajectory", "[hyperbolic][energ
TEST_CASE("Hyperbolic orbit initial conditions", "[hyperbolic][initial]") { TEST_CASE("Hyperbolic orbit initial conditions", "[hyperbolic][initial]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/hyperbolic_comet.toml")); REQUIRE(load_system_config(sim, "tests/configs/hyperbolic_comet.toml"));
@ -174,7 +174,7 @@ TEST_CASE("Hyperbolic orbit asymptotic velocity", "[hyperbolic][asymptotic]") {
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
const double AU = 1.496e11; const double AU = 1.496e11;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/hyperbolic_comet.toml")); REQUIRE(load_system_config(sim, "tests/configs/hyperbolic_comet.toml"));

8
tests/test_invalid_parent_assignment.cpp

@ -14,7 +14,7 @@ TEST_CASE("Invalid parent: Earth should not become child of spacecraft",
"[init][parent][bug]") { "[init][parent][bug]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml")); REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml"));
const int EARTH_IDX = 1; const int EARTH_IDX = 1;
@ -43,7 +43,7 @@ TEST_CASE("Invalid parent: massive bodies never become children of small bodies"
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
const double MASS_THRESHOLD_RATIO = 1000.0; const double MASS_THRESHOLD_RATIO = 1000.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml")); REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml"));
for (int i = 0; i < sim->body_count; i++) { for (int i = 0; i < sim->body_count; i++) {
@ -89,7 +89,7 @@ TEST_CASE("Invalid parent: detect placeholder config values",
"[init][config][validation]") { "[init][config][validation]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml")); REQUIRE(load_system_config(sim, "tests/configs/earth_mars_simple.toml"));
const int EARTH_IDX = 1; const int EARTH_IDX = 1;
@ -117,7 +117,7 @@ TEST_CASE("Mutual SOI: similar mass planets within SOI boundary",
"[init][soi][mutual][edge_case]") { "[init][soi][mutual][edge_case]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/mutual_soi_close.toml")); REQUIRE(load_system_config(sim, "tests/configs/mutual_soi_close.toml"));
const int PLANET_A_IDX = 1; const int PLANET_A_IDX = 1;

50
tests/test_maneuvers.cpp

@ -10,30 +10,25 @@
TEST_CASE("Spacecraft loading from config", "[spacecraft][config]") { TEST_CASE("Spacecraft loading from config", "[spacecraft][config]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 10, TIME_STEP);
SpacecraftState* craft_state = create_spacecraft_state(10);
REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml")); REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml"));
REQUIRE(load_spacecraft_config(craft_state, sim, "tests/configs/spacecraft_test.toml"));
REQUIRE(craft_state->craft_count == 1); REQUIRE(sim->craft_count == 1);
REQUIRE(std::string(craft_state->spacecraft[0].name) == "LEO_Satellite"); REQUIRE(std::string(sim->spacecraft[0].name) == "LEO_Satellite");
REQUIRE(craft_state->spacecraft[0].parent_index == 1); REQUIRE(sim->spacecraft[0].parent_index == 1);
destroy_spacecraft_state(craft_state);
destroy_simulation(sim); destroy_simulation(sim);
} }
TEST_CASE("Prograde burn increases orbital energy", "[spacecraft][burn][prograde]") { TEST_CASE("Prograde burn increases orbital energy", "[spacecraft][burn][prograde]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 10, TIME_STEP);
SpacecraftState* craft_state = create_spacecraft_state(10);
REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml")); REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml"));
REQUIRE(load_spacecraft_config(craft_state, sim, "tests/configs/spacecraft_test.toml"));
Spacecraft* craft = &craft_state->spacecraft[0]; Spacecraft* craft = &sim->spacecraft[0];
CelestialBody* earth = &sim->bodies[1]; CelestialBody* earth = &sim->bodies[1];
double initial_distance = vec3_distance(craft->position, earth->position); double initial_distance = vec3_distance(craft->position, earth->position);
@ -47,27 +42,23 @@ TEST_CASE("Prograde burn increases orbital energy", "[spacecraft][burn][prograde
double sim_time = 0.0; double sim_time = 0.0;
while (sim_time < SECONDS_TO_SIMULATE) { while (sim_time < SECONDS_TO_SIMULATE) {
update_simulation(sim); update_simulation(sim);
update_spacecraft(craft_state, sim);
sim_time += TIME_STEP; sim_time += TIME_STEP;
} }
double final_distance = vec3_distance(craft->position, earth->position); double final_distance = vec3_distance(craft->position, earth->position);
REQUIRE(final_distance > initial_distance); REQUIRE(final_distance > initial_distance);
destroy_spacecraft_state(craft_state);
destroy_simulation(sim); destroy_simulation(sim);
} }
TEST_CASE("Retrograde burn decreases orbital energy", "[spacecraft][burn][retrograde]") { TEST_CASE("Retrograde burn decreases orbital energy", "[spacecraft][burn][retrograde]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 10, TIME_STEP);
SpacecraftState* craft_state = create_spacecraft_state(10);
REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml")); REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml"));
REQUIRE(load_spacecraft_config(craft_state, sim, "tests/configs/spacecraft_test.toml"));
Spacecraft* craft = &craft_state->spacecraft[0]; Spacecraft* craft = &sim->spacecraft[0];
CelestialBody* earth = &sim->bodies[1]; CelestialBody* earth = &sim->bodies[1];
double initial_distance = vec3_distance(craft->position, earth->position); double initial_distance = vec3_distance(craft->position, earth->position);
@ -81,27 +72,23 @@ TEST_CASE("Retrograde burn decreases orbital energy", "[spacecraft][burn][retrog
double sim_time = 0.0; double sim_time = 0.0;
while (sim_time < SECONDS_TO_SIMULATE) { while (sim_time < SECONDS_TO_SIMULATE) {
update_simulation(sim); update_simulation(sim);
update_spacecraft(craft_state, sim);
sim_time += TIME_STEP; sim_time += TIME_STEP;
} }
double final_distance = vec3_distance(craft->position, earth->position); double final_distance = vec3_distance(craft->position, earth->position);
REQUIRE(final_distance < initial_distance); REQUIRE(final_distance < initial_distance);
destroy_spacecraft_state(craft_state);
destroy_simulation(sim); destroy_simulation(sim);
} }
TEST_CASE("Normal burn changes orbital plane", "[spacecraft][burn][normal]") { TEST_CASE("Normal burn changes orbital plane", "[spacecraft][burn][normal]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 10, TIME_STEP);
SpacecraftState* craft_state = create_spacecraft_state(10);
REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml")); REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml"));
REQUIRE(load_spacecraft_config(craft_state, sim, "tests/configs/spacecraft_test.toml"));
Spacecraft* craft = &craft_state->spacecraft[0]; Spacecraft* craft = &sim->spacecraft[0];
double initial_z = craft->local_position.z; double initial_z = craft->local_position.z;
@ -111,26 +98,22 @@ TEST_CASE("Normal burn changes orbital plane", "[spacecraft][burn][normal]") {
double sim_time = 0.0; double sim_time = 0.0;
while (sim_time < SECONDS_TO_SIMULATE) { while (sim_time < SECONDS_TO_SIMULATE) {
update_simulation(sim); update_simulation(sim);
update_spacecraft(craft_state, sim);
sim_time += TIME_STEP; sim_time += TIME_STEP;
} }
REQUIRE(fabs(craft->local_position.z - initial_z) > 1000.0); REQUIRE(fabs(craft->local_position.z - initial_z) > 1000.0);
destroy_spacecraft_state(craft_state);
destroy_simulation(sim); destroy_simulation(sim);
} }
TEST_CASE("Custom burn applies arbitrary delta-v", "[spacecraft][burn][custom]") { TEST_CASE("Custom burn applies arbitrary delta-v", "[spacecraft][burn][custom]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 10, TIME_STEP);
SpacecraftState* craft_state = create_spacecraft_state(10);
REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml")); REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml"));
REQUIRE(load_spacecraft_config(craft_state, sim, "tests/configs/spacecraft_test.toml"));
Spacecraft* craft = &craft_state->spacecraft[0]; Spacecraft* craft = &sim->spacecraft[0];
Vec3 initial_vel = craft->local_velocity; Vec3 initial_vel = craft->local_velocity;
Vec3 delta_v = {10.0, 20.0, 30.0}; Vec3 delta_v = {10.0, 20.0, 30.0};
@ -141,7 +124,6 @@ TEST_CASE("Custom burn applies arbitrary delta-v", "[spacecraft][burn][custom]")
REQUIRE(fabs(craft->local_velocity.y - initial_vel.y - 20.0) < 0.001); REQUIRE(fabs(craft->local_velocity.y - initial_vel.y - 20.0) < 0.001);
REQUIRE(fabs(craft->local_velocity.z - initial_vel.z - 30.0) < 0.001); REQUIRE(fabs(craft->local_velocity.z - initial_vel.z - 30.0) < 0.001);
destroy_spacecraft_state(craft_state);
destroy_simulation(sim); destroy_simulation(sim);
} }
@ -150,13 +132,11 @@ TEST_CASE("Spacecraft propagation maintains stability", "[spacecraft][propagatio
const double DAYS_TO_SIMULATE = 1.0; const double DAYS_TO_SIMULATE = 1.0;
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 10, TIME_STEP);
SpacecraftState* craft_state = create_spacecraft_state(10);
REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml")); REQUIRE(load_system_config(sim, "tests/configs/spacecraft_test.toml"));
REQUIRE(load_spacecraft_config(craft_state, sim, "tests/configs/spacecraft_test.toml"));
Spacecraft* craft = &craft_state->spacecraft[0]; Spacecraft* craft = &sim->spacecraft[0];
CelestialBody* earth = &sim->bodies[1]; CelestialBody* earth = &sim->bodies[1];
double initial_distance = vec3_distance(craft->position, earth->position); double initial_distance = vec3_distance(craft->position, earth->position);
@ -165,7 +145,6 @@ TEST_CASE("Spacecraft propagation maintains stability", "[spacecraft][propagatio
double sim_time = 0.0; double sim_time = 0.0;
while (sim_time < total_time) { while (sim_time < total_time) {
update_simulation(sim); update_simulation(sim);
update_spacecraft(craft_state, sim);
sim_time += TIME_STEP; sim_time += TIME_STEP;
} }
@ -178,6 +157,5 @@ TEST_CASE("Spacecraft propagation maintains stability", "[spacecraft][propagatio
REQUIRE(distance_drift_percent < 1.0); REQUIRE(distance_drift_percent < 1.0);
destroy_spacecraft_state(craft_state);
destroy_simulation(sim); destroy_simulation(sim);
} }

8
tests/test_moon_orbits.cpp

@ -13,7 +13,7 @@ TEST_CASE("Moon orbital stability around Earth", "[moon][earth]") {
const double MAX_SIMULATION_DAYS = 35.0; const double MAX_SIMULATION_DAYS = 35.0;
const double MOON_DISTANCE_FROM_EARTH = 384400000.0; const double MOON_DISTANCE_FROM_EARTH = 384400000.0;
SimulationState* sim = create_simulation(20, TIME_STEP); SimulationState* sim = create_simulation(20, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/solar_system.toml")); REQUIRE(load_system_config(sim, "tests/configs/solar_system.toml"));
@ -98,7 +98,7 @@ TEST_CASE("Galilean moons orbital stability around Jupiter", "[moon][jupiter]")
const double GANYMEDE_PERIOD_DAYS = 7.15; const double GANYMEDE_PERIOD_DAYS = 7.15;
const double CALLISTO_PERIOD_DAYS = 16.69; const double CALLISTO_PERIOD_DAYS = 16.69;
SimulationState* sim = create_simulation(20, TIME_STEP); SimulationState* sim = create_simulation(20, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/solar_system.toml")); REQUIRE(load_system_config(sim, "tests/configs/solar_system.toml"));
@ -162,7 +162,7 @@ TEST_CASE("Titan orbital stability around Saturn", "[moon][saturn]") {
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
const double MAX_SIMULATION_DAYS = 25.0; const double MAX_SIMULATION_DAYS = 25.0;
SimulationState* sim = create_simulation(20, TIME_STEP); SimulationState* sim = create_simulation(20, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/solar_system.toml")); REQUIRE(load_system_config(sim, "tests/configs/solar_system.toml"));
@ -216,7 +216,7 @@ TEST_CASE("Combined solar system with all moons - parent stability", "[moon][int
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
const double MAX_SIMULATION_DAYS = 60.0; const double MAX_SIMULATION_DAYS = 60.0;
SimulationState* sim = create_simulation(20, TIME_STEP); SimulationState* sim = create_simulation(20, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/solar_system.toml")); REQUIRE(load_system_config(sim, "tests/configs/solar_system.toml"));

4
tests/test_orbital_period.cpp

@ -11,7 +11,7 @@ TEST_CASE("Orbital period - Earth (RK4)", "[period][rk4]") {
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
const double MAX_SIMULATION_DAYS = 400.0; const double MAX_SIMULATION_DAYS = 400.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/earth_circular.toml")); REQUIRE(load_system_config(sim, "tests/configs/earth_circular.toml"));
@ -44,7 +44,7 @@ TEST_CASE("Orbital period - Mars (RK4)", "[period][rk4]") {
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
const double MAX_SIMULATION_DAYS = 750.0; const double MAX_SIMULATION_DAYS = 750.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/mars_circular.toml")); REQUIRE(load_system_config(sim, "tests/configs/mars_circular.toml"));

4
tests/test_parabolic_orbit.cpp

@ -12,7 +12,7 @@ TEST_CASE("Parabolic orbit - energy and escape trajectory", "[parabolic][energy]
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
const double AU = 1.496e11; const double AU = 1.496e11;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/parabolic_comet.toml")); REQUIRE(load_system_config(sim, "tests/configs/parabolic_comet.toml"));
@ -104,7 +104,7 @@ TEST_CASE("Parabolic orbit - energy and escape trajectory", "[parabolic][energy]
TEST_CASE("Parabolic orbit initial conditions", "[parabolic][initial]") { TEST_CASE("Parabolic orbit initial conditions", "[parabolic][initial]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/parabolic_comet.toml")); REQUIRE(load_system_config(sim, "tests/configs/parabolic_comet.toml"));

4
tests/test_root_body_transitions.cpp

@ -23,7 +23,7 @@ TEST_CASE("Root body transition - Earth to Sun", "[root][transition]") {
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
const double AU = 1.496e11; const double AU = 1.496e11;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/manual_root_transition.toml")); REQUIRE(load_system_config(sim, "tests/configs/manual_root_transition.toml"));
@ -97,7 +97,7 @@ TEST_CASE("Root body round-trip - Earth -> Sun -> Mars -> Sun", "[root][round-tr
const double DAYS_TO_SIMULATE = 1000.0; const double DAYS_TO_SIMULATE = 1000.0;
const double SECONDS_PER_DAY = 86400.0; const double SECONDS_PER_DAY = 86400.0;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/manual_root_transition.toml")); REQUIRE(load_system_config(sim, "tests/configs/manual_root_transition.toml"));

4
tests/test_soi_transition.cpp

@ -22,7 +22,7 @@ TEST_CASE("SOI transition - Sun to Mars", "[soi][transition]") {
INFO("Note: SOI transitions use simple model (no hysteresis)"); INFO("Note: SOI transitions use simple model (no hysteresis)");
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/soi_transition.toml")); REQUIRE(load_system_config(sim, "tests/configs/soi_transition.toml"));
@ -100,7 +100,7 @@ TEST_CASE("SOI transition - verify SOI radii", "[soi][radii]") {
const double TIME_STEP = 60.0; const double TIME_STEP = 60.0;
const double AU = 1.496e11; const double AU = 1.496e11;
SimulationState* sim = create_simulation(10, TIME_STEP); SimulationState* sim = create_simulation(10, 0, TIME_STEP);
REQUIRE(load_system_config(sim, "tests/configs/soi_transition.toml")); REQUIRE(load_system_config(sim, "tests/configs/soi_transition.toml"));

Loading…
Cancel
Save