Skip to content

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.