Implements sensor fusion for environmental monitoring and adaptive outdoor behavior: - BME280 environmental sensor (temperature, humidity, pressure) - Phone weather API fallback (wind speed, conditions) - Camera-based rain detection (image gradient analysis) - WeatherState.msg with condition bitmask and recommendations - Behavior triggers: rain→seek shelter, wind→reduce speed, extreme temp→warning - Facial expressions: squint (rain), shiver (cold), relax (comfortable) - Real-time publishing: /saltybot/weather (WeatherState), /saltybot/weather_alert (String) - Adaptive thresholds: temp_min_safe, temp_max_safe, wind_threshold Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
79 lines
2.5 KiB
Python
79 lines
2.5 KiB
Python
"""
|
|
Basic tests for weather awareness system.
|
|
"""
|
|
|
|
import pytest
|
|
import numpy as np
|
|
from saltybot_weather.weather_node import RainDetector
|
|
|
|
|
|
class TestRainDetector:
|
|
"""Tests for rain detection algorithm."""
|
|
|
|
def test_rain_detector_init(self):
|
|
"""Test rain detector initialization."""
|
|
detector = RainDetector()
|
|
assert len(detector.history) == 0
|
|
|
|
def test_rain_detector_clear_frame(self):
|
|
"""Test rain detection on clear (low-noise) frame."""
|
|
detector = RainDetector()
|
|
# Create a smooth, clear frame (low gradient)
|
|
frame = np.ones((480, 640, 3), dtype=np.uint8) * 128
|
|
prob = detector.detect(frame)
|
|
# Clear frame should have low rain probability
|
|
assert 0.0 <= prob <= 1.0
|
|
assert prob < 0.3
|
|
|
|
def test_rain_detector_noisy_frame(self):
|
|
"""Test rain detection on noisy (rain-like) frame."""
|
|
detector = RainDetector()
|
|
# Create a noisy frame (high gradient, rain-like)
|
|
np.random.seed(42)
|
|
frame = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)
|
|
prob = detector.detect(frame)
|
|
assert 0.0 <= prob <= 1.0
|
|
|
|
def test_rain_detector_empty_frame(self):
|
|
"""Test rain detection with empty/None frame."""
|
|
detector = RainDetector()
|
|
prob = detector.detect(None)
|
|
assert prob == 0.0
|
|
|
|
def test_rain_detector_smoothing(self):
|
|
"""Test rain detection history smoothing."""
|
|
detector = RainDetector()
|
|
# Simulate alternating clear and noisy frames
|
|
clear = np.ones((480, 640, 3), dtype=np.uint8) * 128
|
|
noisy = np.random.randint(0, 255, (480, 640, 3), dtype=np.uint8)
|
|
|
|
probs = []
|
|
for i in range(10):
|
|
frame = noisy if i % 2 == 0 else clear
|
|
probs.append(detector.detect(frame))
|
|
|
|
# History should have 10 elements
|
|
assert len(detector.history) == 10
|
|
# Probabilities should be bounded
|
|
assert all(0.0 <= p <= 1.0 for p in probs)
|
|
|
|
|
|
class TestWeatherState:
|
|
"""Basic WeatherState message tests."""
|
|
|
|
def test_weather_state_creation(self):
|
|
"""Test creating a WeatherState message."""
|
|
try:
|
|
from saltybot_weather_msgs.msg import WeatherState
|
|
ws = WeatherState()
|
|
ws.temperature = 25.0
|
|
ws.humidity = 60.0
|
|
assert ws.temperature == 25.0
|
|
assert ws.humidity == 60.0
|
|
except ImportError:
|
|
pytest.skip("saltybot_weather_msgs not built")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
pytest.main([__file__])
|