Skip to content

File EventDetector.cpp

File List > astrea > astro > astro > propagation > event_detection > EventDetector.cpp

Go to the documentation of this file

/*
 * The GNU Lesser General Public License (LGPL)
 *
 * Copyright (c) 2025 Jay Iuliano
 *
 * This file is part of Astrea.
 * Astrea is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
 * Astrea is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should
 * have received a copy of the GNU General Public License along with Astrea. If not, see <https://www.gnu.org/licenses/>.
 */

#include <astro/propagation/event_detection/EventDetector.hpp>

#include <mp-units/math.h>

namespace astrea {
namespace astro {

EventDetector::EventDetector(const std::vector<Event>& events) { set_events(events); }

void EventDetector::set_events(const std::vector<Event>& events)
{
    _eventTrackers.clear();
    _eventTrackers.resize(events.size());
    for (std::size_t ii = 0; ii < events.size(); ++ii) {
        _eventTrackers[ii].event            = events[ii];
        _eventTrackers[ii].firstMeasurement = true;
    }
}

std::vector<Event> EventDetector::get_events() const
{
    std::vector<Event> events;
    for (const auto& tracker : _eventTrackers) {
        events.push_back(tracker.event);
    }
    return events;
}

bool EventDetector::detect_events(const Time& time, State& state, Vehicle& vehicle)
{
    bool isTerminal = false;
    const Time eventTime = mp_units::round<mp_units::si::unit_symbols::s>(time); // Round to seconds to avoid numerical issues
    // TODO: Give precision control to user? Might need more machinery to handle this properly
    for (auto& tracker : _eventTrackers) {
        const Event& event = tracker.event;

        // Measure event
        const Unitless value = event.measure_event(eventTime, state, vehicle);

        // Test for a zero-crossing
        const bool eventDetected = detect_event(eventTime, value, tracker);

        if (eventDetected) {
            // Store trigger time
            tracker.detectionTimes.insert(eventTime);

            // Trigger action
            event.trigger_action(eventTime, state, vehicle);

            // Check for termination
            if (event.is_terminal()) { isTerminal = true; }
        }

        // Update the event tracker with the latest time and vehicle data
        tracker.previousTime  = eventTime;
        tracker.previousValue = value;
    }
    return isTerminal;
}

bool EventDetector::detect_event(const Time& time, const Unitless& value, EventTracker& tracker) const
{
    // Have to ignore first measurement to avoid sign assumptions
    static const Unitless zero = 0.0 * mp_units::one;
    if (tracker.firstMeasurement) {
        tracker.firstMeasurement = false;
        return false;
    }
    else if (tracker.previousValue == zero) {
        if (value != zero) { // Previous time was an exact event time so this one can't be
            return false;
        }
        else { // Previous time was an exact event time and so is this one
            return true;
        }
    }
    else {
        // Check for zero crossing
        if ((tracker.previousValue > zero && value <= zero) || (tracker.previousValue < zero && value >= zero)) {
            return true;
        }
    }
    return false;
}

gtl::btree_map<std::string, std::vector<Date>> EventDetector::get_event_times(const Date& epoch) const
{
    gtl::btree_map<std::string, std::vector<Date>> eventTimes;
    for (const auto& tracker : _eventTrackers) {
        std::vector<Date> dates;
        for (const auto& time : tracker.detectionTimes) {
            dates.push_back(epoch + time);
        }
        eventTimes[tracker.event.get_name()] = dates;
    }
    return eventTimes;
}

} // namespace astro
} // namespace astrea