lightning-distance 0.1.0

Estimate how far away lightning is from the flash-to-thunder delay, plus the 30-second safety rule.
Documentation
//! # lightning-distance
//!
//! Estimate how far away a lightning strike is from the delay between the flash and
//! the thunder, and apply the standard 30-second safety rule. The same math behind the
//! [EarlyThunder](https://earlythunder.com/) storm tracker.
//!
//! Sound takes roughly 5 seconds to travel one mile (3 seconds per kilometer).
//!
//! ```
//! use lightning_distance::{miles, is_dangerous};
//! assert!((miles(10.0) - 2.0).abs() < 1e-9);  // 10 s ~ 2 miles
//! assert_eq!(is_dangerous(10.0), true);        // <= 30 s is dangerous
//! ```

/// Seconds for sound to travel one statute mile (~5.0).
pub const SECS_PER_MILE: f64 = 5.0;
/// Seconds for sound to travel one kilometer (~3.0).
pub const SECS_PER_KM: f64 = 3.0;
/// The standard "go inside" threshold (seconds).
pub const DANGER_THRESHOLD_SECS: f64 = 30.0;

/// Distance in miles from the flash-to-thunder delay in seconds.
pub fn miles(seconds: f64) -> f64 { seconds / SECS_PER_MILE }

/// Distance in kilometers from the delay in seconds.
pub fn kilometers(seconds: f64) -> f64 { seconds / SECS_PER_KM }

/// The 30-second rule: if the gap is <= 30 s the storm is close enough to be dangerous.
pub fn is_dangerous(seconds: f64) -> bool { seconds <= DANGER_THRESHOLD_SECS }

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn ten_seconds_is_two_miles() { assert!((miles(10.0) - 2.0).abs() < 1e-9); }
    #[test]
    fn km_matches_mile_conversion() {
        // 1 mile = 1.609 km; 5 s/mile vs 3 s/km -> consistent within rounding
        let m = miles(15.0); let km = kilometers(15.0);
        assert!((km / m - 1.667).abs() < 0.01);
    }
    #[test]
    fn danger_rule() {
        assert!(is_dangerous(30.0));
        assert!(is_dangerous(5.0));
        assert!(!is_dangerous(45.0));
    }
}