Files
gps-denied-onboard/tests/test_satellite.py
T
Yuzviak dd9835c0cd fix(lint): resolve all ruff errors — trailing whitespace, E501, F401
- ruff --fix: removed trailing whitespace (W293), sorted imports (I001)
- Manual: broke long lines (E501) in eskf, rotation, vo, gpr, metric, pipeline, rotation tests
- Removed unused imports (F401) in models.py, schemas/__init__.py
- pyproject.toml: line-length 100→120, E501 ignore for abstract interfaces

ruff check: 0 errors. pytest: 195 passed / 8 skipped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 17:09:47 +03:00

160 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Tests for SatelliteDataManager (F04) — SAT-01/02 and mercator utils (H06)."""
import numpy as np
import pytest
from gps_denied.core.satellite import SatelliteDataManager
from gps_denied.schemas import GPSPoint
from gps_denied.utils import mercator
# ---------------------------------------------------------------
# Mercator utils
# ---------------------------------------------------------------
def test_latlon_to_tile():
lat, lon, zoom = 50.4501, 30.5234, 15
coords = mercator.latlon_to_tile(lat, lon, zoom)
assert coords.zoom == 15
assert coords.x > 0
assert coords.y > 0
def test_tile_to_latlon():
gps = mercator.tile_to_latlon(19131, 10927, 15)
assert 50.0 < gps.lat < 52.0
assert 30.0 < gps.lon < 31.0
def test_tile_bounds():
coords = mercator.TileCoords(x=19131, y=10927, zoom=15)
bounds = mercator.compute_tile_bounds(coords)
assert bounds.nw.lat > bounds.se.lat
assert bounds.nw.lon < bounds.se.lon
assert bounds.gsd > 0
# ---------------------------------------------------------------
# SAT-01: Local tile storage (no HTTP)
# ---------------------------------------------------------------
@pytest.fixture
def satellite_manager(tmp_path):
return SatelliteDataManager(tile_dir=str(tmp_path / "tiles"))
def test_load_local_tile_missing(satellite_manager):
"""Missing tile returns None — no crash."""
coords = mercator.TileCoords(x=0, y=0, zoom=12)
result = satellite_manager.load_local_tile(coords)
assert result is None
def test_save_and_load_local_tile(satellite_manager):
"""SAT-01: saved tile can be read back from the local directory."""
coords = mercator.TileCoords(x=19131, y=10927, zoom=15)
img = np.zeros((256, 256, 3), dtype=np.uint8)
img[:] = [0, 128, 255]
ok = satellite_manager.save_local_tile(coords, img)
assert ok is True
loaded = satellite_manager.load_local_tile(coords)
assert loaded is not None
assert loaded.shape == (256, 256, 3)
def test_mem_cache_hit(satellite_manager):
"""Tile loaded once should be served from memory on second request."""
coords = mercator.TileCoords(x=1, y=1, zoom=10)
img = np.ones((256, 256, 3), dtype=np.uint8) * 42
satellite_manager.save_local_tile(coords, img)
r1 = satellite_manager.load_local_tile(coords)
r2 = satellite_manager.load_local_tile(coords)
assert r1 is r2 # same object = came from mem cache
# ---------------------------------------------------------------
# SAT-02: ESKF ±3σ tile selection
# ---------------------------------------------------------------
def test_select_tiles_small_sigma(satellite_manager):
"""Very tight sigma → single tile covering the position."""
gps = GPSPoint(lat=50.45, lon=30.52)
tiles = satellite_manager.select_tiles_for_eskf_position(gps, sigma_h_m=1.0, zoom=18)
# Should produce at least the center tile
assert len(tiles) >= 1
for t in tiles:
assert t.zoom == 18
def test_select_tiles_large_sigma(satellite_manager):
"""Larger sigma → more tiles returned."""
gps = GPSPoint(lat=50.45, lon=30.52)
small = satellite_manager.select_tiles_for_eskf_position(gps, sigma_h_m=10.0, zoom=18)
large = satellite_manager.select_tiles_for_eskf_position(gps, sigma_h_m=200.0, zoom=18)
assert len(large) >= len(small)
def test_select_tiles_bounding_box(satellite_manager):
"""Selected tiles must span a bounding box that covers ±3σ."""
gps = GPSPoint(lat=49.0, lon=32.0)
sigma = 50.0 # 50 m → 3σ = 150 m
zoom = 18
tiles = satellite_manager.select_tiles_for_eskf_position(gps, sigma_h_m=sigma, zoom=zoom)
assert len(tiles) >= 1
# All returned tiles must be at the requested zoom
assert all(t.zoom == zoom for t in tiles)
# ---------------------------------------------------------------
# SAT-01: Mosaic assembly
# ---------------------------------------------------------------
def test_assemble_mosaic_single(satellite_manager):
"""Single tile → mosaic equals that tile (resized)."""
coords = mercator.TileCoords(x=10, y=10, zoom=15)
img = np.zeros((256, 256, 3), dtype=np.uint8)
mosaic, bounds = satellite_manager.assemble_mosaic([(coords, img)], target_size=256)
assert mosaic.shape == (256, 256, 3)
assert bounds.center is not None
def test_assemble_mosaic_2x2(satellite_manager):
"""2×2 tile grid assembles into a single mosaic."""
mercator.TileCoords(x=10, y=10, zoom=15)
tiles = [
(mercator.TileCoords(x=10, y=10, zoom=15), np.zeros((256, 256, 3), dtype=np.uint8)),
(mercator.TileCoords(x=11, y=10, zoom=15), np.zeros((256, 256, 3), dtype=np.uint8)),
(mercator.TileCoords(x=10, y=11, zoom=15), np.zeros((256, 256, 3), dtype=np.uint8)),
(mercator.TileCoords(x=11, y=11, zoom=15), np.zeros((256, 256, 3), dtype=np.uint8)),
]
mosaic, bounds = satellite_manager.assemble_mosaic(tiles, target_size=512)
assert mosaic.shape == (512, 512, 3)
def test_assemble_mosaic_empty(satellite_manager):
result = satellite_manager.assemble_mosaic([])
assert result is None
# ---------------------------------------------------------------
# Cache helpers (backward compat)
# ---------------------------------------------------------------
def test_cache_tile_compat(satellite_manager):
coords = mercator.TileCoords(x=100, y=100, zoom=12)
img = np.zeros((256, 256, 3), dtype=np.uint8)
assert satellite_manager.cache_tile("f1", coords, img) is True
cached = satellite_manager.get_cached_tile("f1", coords)
assert cached is not None
def test_grid_calculations(satellite_manager):
center = mercator.TileCoords(x=100, y=100, zoom=15)
grid = satellite_manager.get_tile_grid(center, 9)
assert len(grid) == 9
assert any(c.x == 100 and c.y == 100 for c in grid)
new_tiles = satellite_manager.expand_search_grid(center, 9, 25)
assert len(new_tiles) == 16 # 25 - 9