Space Domain Tutorial¶
This tutorial demonstrates how to simulate satellites using JaguarEngine's Space domain.
Prerequisites¶
- JaguarEngine installed and configured
- Basic understanding of orbital mechanics
- C++ development environment
Tutorial: Satellite Simulation¶
Step 1: Create the Engine¶
#include <jaguar/jaguar.h>
using namespace jaguar;
int main() {
Engine engine;
if (!engine.initialize()) {
std::cerr << "Failed to initialize engine\n";
return 1;
}
Step 2: Create a Satellite Entity¶
Step 3: Define the Orbit¶
// Define orbital elements (ISS-like orbit)
domain::space::OrbitalElements orbit;
orbit.semi_major_axis = constants::EARTH_RADIUS + 420000.0; // 420 km altitude
orbit.eccentricity = 0.0001; // Nearly circular
orbit.inclination = 51.6 * constants::DEG_TO_RAD;
orbit.raan = 0.0;
orbit.arg_of_perigee = 0.0;
orbit.true_anomaly = 0.0;
// Convert to state vectors
Vec3 pos_eci, vel_eci;
orbit.to_state_vector(pos_eci, vel_eci);
// Set initial state
physics::EntityState state;
state.position = pos_eci;
state.velocity = vel_eci;
state.mass = 420000.0; // ISS mass (kg)
// Inertia tensor
state.inertia = Mat3x3::identity();
state.inertia.data[0][0] = 1e8;
state.inertia.data[1][1] = 1e8;
state.inertia.data[2][2] = 1e8;
engine.set_entity_state(satellite, state);
Step 4: Create Physics Models¶
// Create gravity model (J2 perturbation)
domain::space::GravityModel gravity;
gravity.set_fidelity(1); // J2 level
// Create atmospheric drag model
domain::space::AtmosphericDragModel drag;
drag.set_cd(2.2);
drag.set_area(2500.0); // ISS cross-section (m²)
// Create atmosphere model
domain::space::JB08AtmosphereModel atm;
atm.set_space_weather(150.0, 145.0, 15.0); // Moderate solar activity
Step 5: Run the Simulation¶
// Simulation parameters
Real dt = 60.0; // 1 minute steps
Real duration = orbit.period(); // One orbital period
std::cout << "Time(min), Altitude(km), Lat(deg), Lon(deg)\n";
for (Real t = 0; t < duration; t += dt) {
auto state = engine.get_entity_state(satellite);
// Build environment
environment::Environment env;
env.altitude = state.position.norm() - constants::EARTH_RADIUS;
// Compute forces
physics::EntityForces forces;
forces.clear();
gravity.compute_forces(state, env, dt, forces);
// Apply drag only in lower atmosphere
if (env.altitude < 800000.0) {
drag.compute_forces(state, env, dt, forces);
}
// Apply forces and step
engine.apply_forces(satellite, forces);
engine.step(dt);
// Output telemetry every 5 minutes
if (std::fmod(t, 300.0) < dt) {
state = engine.get_entity_state(satellite);
// Convert to geodetic
using namespace transforms;
Vec3 pos_ecef = eci_to_ecef(state.position, t);
Real lat, lon, alt;
ecef_to_geodetic(pos_ecef, lat, lon, alt);
std::cout << t / 60.0 << ", "
<< alt / 1000.0 << ", "
<< lat * constants::RAD_TO_DEG << ", "
<< lon * constants::RAD_TO_DEG << "\n";
}
}
engine.shutdown();
return 0;
}
Tutorial: TLE Propagation¶
Using SGP4 with TLE Data¶
// Parse TLE
std::string line1 = "1 25544U 98067A 21275.52422453 .00001234 00000-0 28888-4 0 9990";
std::string line2 = "2 25544 51.6442 27.4345 0003542 211.7212 148.3510 15.48919914305213";
domain::space::TLE tle;
if (!tle.parse(line1, line2)) {
std::cerr << "Failed to parse TLE\n";
return 1;
}
// Initialize SGP4 propagator
domain::space::SGP4Propagator sgp4;
if (!sgp4.initialize(tle)) {
std::cerr << "Failed to initialize SGP4\n";
return 1;
}
// Propagate for 24 hours
for (Real minutes = 0; minutes < 24 * 60; minutes += 1.0) {
Vec3 pos_km, vel_kms;
sgp4.propagate(minutes, pos_km, vel_kms);
// Convert to SI
Vec3 pos_m = pos_km * 1000.0;
Vec3 vel_ms = vel_kms * 1000.0;
// Use position/velocity...
}
Tutorial: Ground Station Visibility¶
Computing Satellite Passes¶
struct Pass {
Real start_time;
Real end_time;
Real max_elevation;
};
std::vector<Pass> find_passes(
domain::space::SGP4Propagator& sgp4,
const Vec3& station_ecef,
Real start_jd,
Real duration_days,
Real min_elevation_deg)
{
std::vector<Pass> passes;
Real min_elev_rad = min_elevation_deg * constants::DEG_TO_RAD;
Real dt = 1.0; // 1 minute steps
bool in_pass = false;
Pass current_pass;
for (Real minutes = 0; minutes < duration_days * 24 * 60; minutes += dt) {
// Get satellite position
Vec3 pos_km, vel_kms;
sgp4.propagate(minutes, pos_km, vel_kms);
Vec3 pos_eci = pos_km * 1000.0;
// Convert to ECEF
Real jd = start_jd + minutes / (24 * 60);
Vec3 sat_ecef = transforms::eci_to_ecef(pos_eci, jd);
// Compute elevation angle
Vec3 range = sat_ecef - station_ecef;
Vec3 up = station_ecef.normalized();
Real elevation = std::asin(range.dot(up) / range.norm());
if (elevation > min_elev_rad) {
if (!in_pass) {
// Start of pass
in_pass = true;
current_pass.start_time = minutes;
current_pass.max_elevation = elevation;
} else {
// During pass
if (elevation > current_pass.max_elevation) {
current_pass.max_elevation = elevation;
}
}
} else {
if (in_pass) {
// End of pass
current_pass.end_time = minutes;
passes.push_back(current_pass);
in_pass = false;
}
}
}
return passes;
}
Tutorial: Constellation Simulation¶
GPS Constellation¶
// Create GPS constellation (24 satellites, 6 orbital planes)
std::vector<EntityId> gps_satellites;
for (int plane = 0; plane < 6; ++plane) {
for (int slot = 0; slot < 4; ++slot) {
std::string name = "GPS-" + std::to_string(plane * 4 + slot + 1);
EntityId sat = engine.create_entity(name, Domain::Space);
// GPS orbit parameters
domain::space::OrbitalElements orbit;
orbit.semi_major_axis = 26560000.0; // GPS altitude
orbit.eccentricity = 0.0;
orbit.inclination = 55.0 * DEG_TO_RAD;
orbit.raan = plane * 60.0 * DEG_TO_RAD; // 60° separation
orbit.arg_of_perigee = 0.0;
orbit.true_anomaly = slot * 90.0 * DEG_TO_RAD; // 90° spacing
Vec3 pos, vel;
orbit.to_state_vector(pos, vel);
physics::EntityState state;
state.position = pos;
state.velocity = vel;
state.mass = 1080.0; // GPS Block IIR mass
engine.set_entity_state(sat, state);
gps_satellites.push_back(sat);
}
}
XML Configuration¶
Spacecraft Definition¶
<?xml version="1.0" encoding="UTF-8"?>
<entity type="spacecraft" name="GPS-IIR">
<mass_balance>
<dry_mass unit="kg">1080</dry_mass>
<fuel_mass unit="kg">350</fuel_mass>
</mass_balance>
<aerodynamics>
<drag_coefficient>2.2</drag_coefficient>
<cross_section unit="m2">10.0</cross_section>
</aerodynamics>
<srp>
<reflectivity>0.8</reflectivity>
<area unit="m2">15.0</area>
</srp>
<orbit>
<semi_major_axis unit="km">26560</semi_major_axis>
<eccentricity>0.0</eccentricity>
<inclination unit="deg">55.0</inclination>
</orbit>
</entity>
Common Issues¶
TLE Accuracy¶
Problem: Position errors grow over time
Solution: TLE accuracy degrades; use recent TLEs (< 1-2 weeks old)
Atmospheric Drag Variations¶
Problem: Orbit decay rate varies unexpectedly
Solution: Update space weather parameters (F10.7, Ap)
Next Steps¶
- Air Domain Tutorial - Aircraft simulation
- Land Domain Tutorial - Ground vehicle simulation
- Sea Domain Tutorial - Naval simulation
- API Reference - Space domain API