Event Detection¶
Astrea provides a comprehensive event detection system that enables monitoring and responding to specific conditions during orbit propagation. Events can trigger actions, halt propagation, or record state information when user-defined conditions are met.
Event System Architecture¶
The event system is built around several key concepts:
- Event Interface: Defines condition detection and response behavior
- Event Detector: Manages event monitoring during propagation
- Event Types: Predefined and user-defined event conditions
- Event Actions: Responses triggered when events occur
Event Interface¶
All events implement a standardized interface using C++20 concepts:
#include <astro/propagation/event_detection/Event.hpp>
// Event detection concept
template <typename T>
concept HasMeasureEvent = requires(const T event, const Time& time, const State& state, const Vehicle& vehicle) {
{ event.measure_event(time, state, vehicle) } -> std::same_as<Unitless>;
};
// Event naming concept
template <typename T>
concept HasGetName = requires(const T event) {
{ event.get_name() } -> std::same_as<std::string>;
};
// Terminal event concept
template <typename T>
concept HasIsTerminal = requires(const T event) {
{ event.is_terminal() } -> std::same_as<bool>;
};
Basic Event Implementation¶
Custom events inherit from the Event base class:
class AltitudeEvent : public Event {
public:
AltitudeEvent(Distance targetAltitude, bool isTerminal = false)
: targetAltitude_(targetAltitude), terminal_(isTerminal) {}
// Measure event condition (negative when condition is met)
Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const override {
Distance currentAltitude = state.get_altitude();
return (currentAltitude - targetAltitude_) / (1.0 * km); // Normalized difference
}
std::string get_name() const override {
return "Altitude Event";
}
bool is_terminal() const override {
return terminal_;
}
private:
Distance targetAltitude_;
bool terminal_;
};
Event Detector¶
The EventDetector manages event monitoring during propagation:
#include <astro/propagation/event_detection/EventDetector.hpp>
// Create event detector
EventDetector detector;
// Add events to monitor
auto altitudeEvent = std::make_unique<AltitudeEvent>(300.0 * km, true); // Terminal
auto apoapsisEvent = std::make_unique<ApoapsisEvent>(); // Non-terminal
detector.add_event(std::move(altitudeEvent));
detector.add_event(std::move(apoapsisEvent));
Common Event Types¶
Altitude Events¶
// Monitor specific altitude
class AltitudeEvent : public Event {
public:
AltitudeEvent(Distance altitude, EventType type = EventType::CROSSING)
: targetAltitude_(altitude), type_(type) {}
Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const override {
Distance currentAlt = magnitude(state.get_position()) - EARTH_RADIUS;
switch(type_) {
case EventType::CROSSING:
return (currentAlt - targetAltitude_) / (1.0 * km);
case EventType::MINIMUM:
return (targetAltitude_ - currentAlt) / (1.0 * km); // Detect minimum
case EventType::MAXIMUM:
return (currentAlt - targetAltitude_) / (1.0 * km); // Detect maximum
}
}
};
Orbital Element Events¶
// Apoapsis detection
class ApoapsisEvent : public Event {
public:
Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const override {
// True anomaly derivative - zero at apoapsis
auto elements = state.get_elements<Keplerian>();
return elements.get_true_anomaly_rate() / (1.0 * rad / s);
}
std::string get_name() const override { return "Apoapsis"; }
bool is_terminal() const override { return false; }
};
// Periapsis detection
class PeriapsisEvent : public Event {
public:
Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const override {
auto elements = state.get_elements<Keplerian>();
return elements.get_true_anomaly_rate() / (1.0 * rad / s);
}
std::string get_name() const override { return "Periapsis"; }
bool is_terminal() const override { return false; }
};
Time-Based Events¶
// Absolute time event
class AbsoluteTimeEvent : public Event {
public:
AbsoluteTimeEvent(const Time& targetTime)
: targetTime_(targetTime) {}
Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const override {
return (time - targetTime_) / (1.0 * s);
}
std::string get_name() const override { return "Absolute Time"; }
bool is_terminal() const override { return true; }
private:
Time targetTime_;
};
// Duration event
class DurationEvent : public Event {
public:
DurationEvent(const Time& startTime, const Time& duration)
: startTime_(startTime), duration_(duration) {}
Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const override {
return ((time - startTime_) - duration_) / (1.0 * s);
}
std::string get_name() const override { return "Duration"; }
bool is_terminal() const override { return true; }
private:
Time startTime_;
Time duration_;
};
Geometric Events¶
// Eclipse detection
class EclipseEvent : public Event {
public:
EclipseEvent(const AstrodynamicsSystem& system)
: system_(system) {}
Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const override {
// Calculate sun angle relative to Earth shadow
CartesianVector<Distance, ECI> sunPos = system_.get_sun_position(time);
CartesianVector<Distance, ECI> satPos = state.get_position();
// Shadow cone calculation
double shadowAngle = calculate_shadow_angle(sunPos, satPos);
return shadowAngle; // Negative when in eclipse
}
std::string get_name() const override { return "Eclipse"; }
bool is_terminal() const override { return false; }
private:
const AstrodynamicsSystem& system_;
};
Event Actions¶
Events can trigger custom actions when detected:
class ManeuverEvent : public Event {
public:
ManeuverEvent(const CartesianVector<Acceleration, ECI>& deltaV)
: deltaV_(deltaV) {}
// Event action triggered after detection
void trigger_action(State& state, Vehicle& vehicle) const override {
// Apply instantaneous velocity change
auto currentVel = state.get_velocity();
auto newVel = currentVel + deltaV_ * (1.0 * s); // Convert acceleration to velocity
state.set_velocity(newVel);
std::cout << "Maneuver executed: ΔV = " << magnitude(deltaV_) << std::endl;
}
std::string get_name() const override { return "Maneuver"; }
bool is_terminal() const override { return false; }
private:
CartesianVector<Acceleration, ECI> deltaV_;
};
Integration with Propagation¶
Events integrate seamlessly with numerical propagators:
#include <astro/propagation/numerical/RungeKutta.hpp>
// Set up propagator with event detection
RungeKutta4 integrator;
EventDetector detector;
// Add events
detector.add_event(std::make_unique<AltitudeEvent>(200.0 * km, true));
detector.add_event(std::make_unique<EclipseEvent>(system));
detector.add_event(std::make_unique<ManeuverEvent>(deltaVVector));
// Propagate with event monitoring
State initialState = /* ... */;
Time propagationTime = 2.0 * day;
PropagationResult result = integrator.propagate_with_events(
initialState, spacecraft, equations, detector, propagationTime
);
// Check which events were detected
for (const auto& detectedEvent : result.detected_events) {
std::cout << "Event detected: " << detectedEvent.name
<< " at time: " << detectedEvent.time << std::endl;
}
Event Timing Accuracy¶
The event detector uses root-finding algorithms for precise timing:
// Configure event detection accuracy
detector.set_tolerance(1e-12); // Root-finding tolerance
detector.set_max_iterations(100); // Maximum iterations
detector.set_time_precision(0.001 * s); // Time precision
Advanced Event Features¶
Composite Events¶
// Multiple condition events
class CompositeEvent : public Event {
public:
Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const override {
Distance altitude = state.get_altitude();
Angle latitude = state.get_latitude();
// Event occurs when both conditions are satisfied
bool altitudeCondition = altitude > 400.0 * km;
bool latitudeCondition = abs(latitude) < 30.0 * deg;
// Return negative when both conditions are true
return altitudeCondition && latitudeCondition ? -1.0 : 1.0;
}
};
State-Dependent Events¶
// Events that depend on vehicle state
class FuelDepletionEvent : public Event {
public:
Unitless measure_event(const Time& time, const State& state, const Vehicle& vehicle) const override {
Mass currentFuelMass = vehicle.get_fuel_mass();
Mass minFuelMass = 10.0 * kg;
return (currentFuelMass - minFuelMass) / (1.0 * kg);
}
std::string get_name() const override { return "Fuel Depletion"; }
bool is_terminal() const override { return true; }
};
This event detection system provides powerful capabilities for monitoring complex orbital conditions and automatically responding to mission-critical events during propagation.