118 lines
3.2 KiB
Python
118 lines
3.2 KiB
Python
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
|