104 lines
3.3 KiB
Python
104 lines
3.3 KiB
Python
from math import isclose
|
|
|
|
from app.core.surface import SurfaceProfile, SurfaceSample
|
|
from app.models.coverage import LinkRx, LinkTx
|
|
from app.models.link import LinkBudgetRequest
|
|
from app.services import link as link_service
|
|
|
|
|
|
def test_link_budget_includes_vegetation_loss(monkeypatch) -> None:
|
|
monkeypatch.setattr(link_service, "vegetation_loss_along", lambda *args, **kwargs: 5.0)
|
|
monkeypatch.setattr(
|
|
link_service,
|
|
"surface_profile_from_points",
|
|
lambda points, *args, **kwargs: SurfaceProfile(
|
|
distance_m=points[-1].distance_m,
|
|
samples=[
|
|
SurfaceSample(
|
|
i=index,
|
|
lat=point.lat,
|
|
lon=point.lon,
|
|
distance_m=point.distance_m,
|
|
ground_m=0,
|
|
building_m=0,
|
|
canopy_m=0,
|
|
surface_m=0,
|
|
)
|
|
for index, point in enumerate(points)
|
|
],
|
|
),
|
|
)
|
|
request = LinkBudgetRequest(
|
|
tx=LinkTx(lat=60.17, lon=24.94, height_agl=30, power_dbm=37, gain_dbi=8),
|
|
rx=LinkRx(
|
|
lat=60.25,
|
|
lon=25.10,
|
|
height_agl=2,
|
|
gain_dbi=2,
|
|
sensitivity_dbm=-110,
|
|
),
|
|
frequency_mhz=433,
|
|
model="manual",
|
|
include_vegetation=True,
|
|
)
|
|
|
|
result = link_service.link_budget(request)
|
|
|
|
assert result.vegetation_db == 5.0
|
|
assert result.los_clear is result.fresnel_clear
|
|
assert result.fresnel_violations_count >= 0
|
|
assert isclose(
|
|
result.total_loss_db,
|
|
result.fspl_db + result.diffraction_db + 5.0,
|
|
)
|
|
|
|
|
|
def test_link_budget_includes_diffraction_loss(monkeypatch) -> None:
|
|
monkeypatch.setattr(link_service, "vegetation_loss_along", lambda *args, **kwargs: 0.0)
|
|
|
|
def fake_profile(points, *args, **kwargs):
|
|
midpoint = len(points) // 2
|
|
samples = []
|
|
for index, point in enumerate(points):
|
|
obstacle = 20.0 if index == midpoint else 0.0
|
|
samples.append(
|
|
SurfaceSample(
|
|
i=index,
|
|
lat=point.lat,
|
|
lon=point.lon,
|
|
distance_m=point.distance_m,
|
|
ground_m=obstacle,
|
|
building_m=0,
|
|
canopy_m=0,
|
|
surface_m=obstacle,
|
|
)
|
|
)
|
|
return SurfaceProfile(distance_m=points[-1].distance_m, samples=samples)
|
|
|
|
monkeypatch.setattr(link_service, "surface_profile_from_points", fake_profile)
|
|
request = LinkBudgetRequest(
|
|
tx=LinkTx(lat=60.17, lon=24.94, height_agl=0, power_dbm=37, gain_dbi=8),
|
|
rx=LinkRx(
|
|
lat=60.25,
|
|
lon=25.10,
|
|
height_agl=0,
|
|
gain_dbi=2,
|
|
sensitivity_dbm=-110,
|
|
),
|
|
frequency_mhz=433,
|
|
model="manual",
|
|
include_buildings=False,
|
|
include_vegetation=False,
|
|
)
|
|
|
|
result = link_service.link_budget(request)
|
|
|
|
assert result.diffraction_db > 0
|
|
assert isclose(result.total_loss_db, result.fspl_db + result.diffraction_db)
|
|
assert result.fresnel_clear is False
|
|
assert result.los_clear is False
|
|
assert result.geometric_los is False
|
|
assert result.fresnel_violations_count > 0
|
|
assert result.geometric_obstructions_count > 0
|
|
assert result.worst_obstruction is not None
|