""" Detailed unit tests for the Free Space Path Loss model. """ import sys import os import math sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) from app.propagation.base import PropagationInput from app.propagation.free_space import FreeSpaceModel def make_input(**kwargs) -> PropagationInput: defaults = { "frequency_mhz": 1800, "distance_m": 1000, "tx_height_m": 30, "rx_height_m": 1.5, "environment": "urban", } defaults.update(kwargs) return PropagationInput(**defaults) class TestFreeSpaceModel: def test_formula_accuracy(self): """FSPL = 20*log10(d_km) + 20*log10(f_MHz) + 32.45""" model = FreeSpaceModel() # At 1km, 1000MHz: 20*0 + 20*60 + 32.45 = 92.45 dB out = model.calculate(make_input(distance_m=1000, frequency_mhz=1000)) expected = 32.45 + 20 * math.log10(1.0) + 20 * math.log10(1000) assert abs(out.path_loss_db - expected) < 0.1 def test_6db_per_distance_doubling(self): model = FreeSpaceModel() loss_1 = model.calculate(make_input(distance_m=1000)).path_loss_db loss_2 = model.calculate(make_input(distance_m=2000)).path_loss_db assert abs((loss_2 - loss_1) - 6.02) < 0.1 def test_6db_per_frequency_doubling(self): model = FreeSpaceModel() loss_1 = model.calculate(make_input(frequency_mhz=900)).path_loss_db loss_2 = model.calculate(make_input(frequency_mhz=1800)).path_loss_db assert abs((loss_2 - loss_1) - 6.02) < 0.1 def test_always_los(self): model = FreeSpaceModel() out = model.calculate(make_input()) assert out.is_los is True def test_model_name(self): model = FreeSpaceModel() assert model.name == "Free-Space" def test_wide_frequency_range(self): model = FreeSpaceModel() assert model.is_valid_for(make_input(frequency_mhz=1)) assert model.is_valid_for(make_input(frequency_mhz=100000)) def test_very_short_distance(self): model = FreeSpaceModel() out = model.calculate(make_input(distance_m=10)) assert out.path_loss_db > 0 assert out.path_loss_db < 80 def test_very_long_distance(self): model = FreeSpaceModel() out = model.calculate(make_input(distance_m=100000)) assert out.path_loss_db > 120 if __name__ == "__main__": instance = TestFreeSpaceModel() for method_name in [m for m in dir(instance) if m.startswith("test_")]: try: getattr(instance, method_name)() print(f" PASS {method_name}") except Exception as e: print(f" FAIL {method_name}: {e}") print("\nAll tests completed.")