from math import isclose import pytest from app.core.antenna import AntennaPattern, gain from app.core.diffraction import bullington_loss, knife_edge_loss, knife_edge_v from app.core.fresnel import earth_bulge, fresnel_radius, wavelength from app.core.propagation import fspl from app.core.surface import SurfaceProfile, SurfaceSample from app.core.vegetation import P833Coefficients, p833_attenuation def test_fspl_reference_value() -> None: assert isclose(fspl(433, 12.45), 107.08, abs_tol=0.05) def test_fresnel_radius_reference_value() -> None: lmbda = wavelength(433_000_000) assert isclose(fresnel_radius(lmbda, 6_225, 6_225), 46.45, abs_tol=0.05) def test_earth_bulge_reference_value() -> None: assert isclose(earth_bulge(6_225, 6_225, 1.333), 2.28, abs_tol=0.02) @pytest.mark.parametrize( ("v", "expected"), [ (-0.79, 0.0), (0.0, 6.03), (1.0, 13.93), ], ) def test_knife_edge_loss_reference_values(v: float, expected: float) -> None: assert isclose(knife_edge_loss(v), expected, abs_tol=0.05) def test_bullington_loss_uses_dominant_equivalent_edge() -> None: profile = SurfaceProfile( distance_m=10_000, samples=[ SurfaceSample( i=0, lat=0, lon=0, distance_m=0, ground_m=0, building_m=0, canopy_m=0, surface_m=0, ), SurfaceSample( i=1, lat=0, lon=0, distance_m=5_000, ground_m=20, building_m=0, canopy_m=0, surface_m=20, ), SurfaceSample( i=2, lat=0, lon=0, distance_m=10_000, ground_m=0, building_m=0, canopy_m=0, surface_m=0, ), ], ) v = knife_edge_v(20, 5_000, 5_000, 433_000_000) assert isclose(bullington_loss(profile, 0, 0, 433_000_000), knife_edge_loss(v)) def test_bullington_loss_does_not_sum_every_dem_sample() -> None: samples = [ SurfaceSample( i=i, lat=0, lon=0, distance_m=i * 100, ground_m=5 if 20 <= i <= 80 else 0, building_m=0, canopy_m=0, surface_m=5 if 20 <= i <= 80 else 0, ) for i in range(101) ] profile = SurfaceProfile(distance_m=10_000, samples=samples) assert bullington_loss(profile, 0, 0, 433_000_000) < 30 def test_p833_formula_with_supplied_coefficients() -> None: attenuation = p833_attenuation( depth_m=100, freq_hz=433_000_000, forest_type="coniferous", coefficients=P833Coefficients(gamma_db_per_m=0.2, max_attenuation_db=30), ) assert isclose(attenuation, 14.60, abs_tol=0.05) def test_sector_antenna_gain_peaks_at_boresight() -> None: pattern = AntennaPattern( pattern="sector", azimuth_deg=90, gain_dbi=8, beamwidth_h=65, beamwidth_v=15, ) assert gain(pattern, 90, 0) == 8 assert gain(pattern, 270, 0) < 8