import astrora._core as core
import numpy as np
import pytest
class TestVisibilityCircle:
def test_basic_visibility_circle(self):
result = core.visibility_circle(
lat_sub_deg=0.0,
lon_sub_deg=0.0,
altitude_km=400.0,
min_elevation_deg=0.0,
num_points=32,
)
assert "latitudes" in result
assert "longitudes" in result
assert "num_points" in result
assert "angular_radius_deg" in result
assert result["num_points"] == 32
assert len(result["latitudes"]) == 32
assert len(result["longitudes"]) == 32
assert 19.0 < result["angular_radius_deg"] < 21.0
def test_visibility_circle_with_elevation(self):
result_0deg = core.visibility_circle(
lat_sub_deg=0.0,
lon_sub_deg=0.0,
altitude_km=400.0,
min_elevation_deg=0.0,
num_points=32,
)
result_10deg = core.visibility_circle(
lat_sub_deg=0.0,
lon_sub_deg=0.0,
altitude_km=400.0,
min_elevation_deg=10.0,
num_points=32,
)
result_45deg = core.visibility_circle(
lat_sub_deg=0.0,
lon_sub_deg=0.0,
altitude_km=400.0,
min_elevation_deg=45.0,
num_points=32,
)
assert result_0deg["angular_radius_deg"] > result_10deg["angular_radius_deg"]
assert result_10deg["angular_radius_deg"] > result_45deg["angular_radius_deg"]
def test_visibility_circle_default_points(self):
result = core.visibility_circle(
lat_sub_deg=0.0, lon_sub_deg=0.0, altitude_km=400.0, min_elevation_deg=10.0
)
assert result["num_points"] == 64
assert len(result["latitudes"]) == 64
def test_visibility_circle_different_locations(self):
result_eq = core.visibility_circle(
lat_sub_deg=0.0,
lon_sub_deg=0.0,
altitude_km=400.0,
min_elevation_deg=10.0,
num_points=32,
)
result_mid = core.visibility_circle(
lat_sub_deg=45.0,
lon_sub_deg=10.0,
altitude_km=400.0,
min_elevation_deg=10.0,
num_points=32,
)
result_polar = core.visibility_circle(
lat_sub_deg=85.0,
lon_sub_deg=0.0,
altitude_km=400.0,
min_elevation_deg=10.0,
num_points=32,
)
assert result_eq["num_points"] == 32
assert result_mid["num_points"] == 32
assert result_polar["num_points"] == 32
assert abs(result_eq["angular_radius_deg"] - result_mid["angular_radius_deg"]) < 0.1
assert abs(result_eq["angular_radius_deg"] - result_polar["angular_radius_deg"]) < 0.1
def test_visibility_circle_longitude_range(self):
result = core.visibility_circle(
lat_sub_deg=0.0,
lon_sub_deg=175.0, altitude_km=400.0,
min_elevation_deg=10.0,
num_points=64,
)
assert np.all(result["longitudes"] >= -180.0)
assert np.all(result["longitudes"] <= 180.0)
def test_visibility_circle_latitude_range(self):
result = core.visibility_circle(
lat_sub_deg=50.0,
lon_sub_deg=0.0,
altitude_km=400.0,
min_elevation_deg=10.0,
num_points=64,
)
assert np.all(result["latitudes"] >= -90.0)
assert np.all(result["latitudes"] <= 90.0)
class TestCoverageArea:
def test_basic_coverage_area(self):
area = core.coverage_area(400.0, 10.0)
assert 5_000_000 < area < 7_000_000
def test_coverage_area_increases_with_altitude(self):
area_leo = core.coverage_area(400.0, 10.0) area_meo = core.coverage_area(20000.0, 10.0) area_geo = core.coverage_area(35786.0, 10.0)
assert area_leo < area_meo < area_geo
def test_coverage_area_decreases_with_elevation(self):
area_0deg = core.coverage_area(400.0, 0.0) area_10deg = core.coverage_area(400.0, 10.0) area_45deg = core.coverage_area(400.0, 45.0)
assert area_0deg > area_10deg > area_45deg
def test_coverage_area_zero_elevation(self):
area_horizon = core.coverage_area(400.0, 0.0)
area_10deg = core.coverage_area(400.0, 10.0)
assert area_horizon > area_10deg
assert 15_000_000 < area_horizon < 25_000_000
def test_coverage_area_high_altitude(self):
area_geo = core.coverage_area(35786.0, 5.0)
earth_area = 510_000_000
coverage_fraction = area_geo / earth_area
assert 0.3 < coverage_fraction < 0.45
class TestCoveragePercentage:
def test_basic_coverage_percentage(self):
percentage = core.coverage_percentage(90.0, 1440.0)
expected = 90.0 / 1440.0
assert abs(percentage - expected) < 1e-10
def test_coverage_percentage_full(self):
percentage = core.coverage_percentage(100.0, 100.0)
assert percentage == 1.0
def test_coverage_percentage_zero(self):
percentage = core.coverage_percentage(0.0, 100.0)
assert percentage == 0.0
def test_coverage_percentage_partial(self):
percentage_6h = core.coverage_percentage(360.0, 1440.0)
assert abs(percentage_6h - 0.25) < 1e-10
percentage_12h = core.coverage_percentage(720.0, 1440.0)
assert abs(percentage_12h - 0.5) < 1e-10
class TestIntegration:
def test_coverage_area_matches_circle(self):
altitude = 400.0
min_elevation = 10.0
area = core.coverage_area(altitude, min_elevation)
circle = core.visibility_circle(
lat_sub_deg=0.0,
lon_sub_deg=0.0,
altitude_km=altitude,
min_elevation_deg=min_elevation,
num_points=64,
)
angular_radius_deg = circle["angular_radius_deg"]
angular_radius_rad = np.radians(angular_radius_deg)
R = 6378.137 expected_area = 2 * np.pi * R**2 * (1 - np.cos(angular_radius_rad))
assert abs(area - expected_area) / expected_area < 0.01
def test_iss_realistic_scenario(self):
altitude = 408.0 min_elevation = 10.0
circle = core.visibility_circle(
lat_sub_deg=28.5, lon_sub_deg=-80.6, altitude_km=altitude,
min_elevation_deg=min_elevation,
num_points=64,
)
area = core.coverage_area(altitude, min_elevation)
assert 10.0 < circle["angular_radius_deg"] < 15.0
assert 5_000_000 < area < 8_000_000
assert len(circle["latitudes"]) == 64
assert len(circle["longitudes"]) == 64
def test_geo_satellite_coverage(self):
altitude = 35786.0 min_elevation = 5.0
area = core.coverage_area(altitude, min_elevation)
circle = core.visibility_circle(
lat_sub_deg=0.0,
lon_sub_deg=0.0,
altitude_km=altitude,
min_elevation_deg=min_elevation,
num_points=128,
)
assert area > 150_000_000 assert circle["angular_radius_deg"] > 70.0
def test_polar_orbit_coverage(self):
altitude = 600.0
min_elevation = 5.0
circle_pole = core.visibility_circle(
lat_sub_deg=90.0,
lon_sub_deg=0.0,
altitude_km=altitude,
min_elevation_deg=min_elevation,
num_points=64,
)
circle_eq = core.visibility_circle(
lat_sub_deg=0.0,
lon_sub_deg=0.0,
altitude_km=altitude,
min_elevation_deg=min_elevation,
num_points=64,
)
assert abs(circle_pole["angular_radius_deg"] - circle_eq["angular_radius_deg"]) < 0.1
if __name__ == "__main__":
pytest.main([__file__, "-v"])