""" Detailed unit tests for the Okumura-Hata model. """ import sys import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) from app.propagation.base import PropagationInput from app.propagation.okumura_hata import OkumuraHataModel def make_input(**kwargs) -> PropagationInput: defaults = { "frequency_mhz": 900, "distance_m": 5000, "tx_height_m": 30, "rx_height_m": 1.5, "environment": "urban", } defaults.update(kwargs) return PropagationInput(**defaults) class TestOkumuraHata: def test_urban_typical_range(self): model = OkumuraHataModel() out = model.calculate(make_input()) # 900MHz, 5km, urban: expect ~130-155 dB assert 120 < out.path_loss_db < 160 def test_environment_ordering(self): """Urban > suburban > rural path loss.""" model = OkumuraHataModel() urban = model.calculate(make_input(environment="urban")).path_loss_db suburban = model.calculate(make_input(environment="suburban")).path_loss_db rural = model.calculate(make_input(environment="rural")).path_loss_db assert urban > suburban > rural def test_distance_increases_loss(self): model = OkumuraHataModel() loss_1 = model.calculate(make_input(distance_m=2000)).path_loss_db loss_5 = model.calculate(make_input(distance_m=5000)).path_loss_db loss_10 = model.calculate(make_input(distance_m=10000)).path_loss_db assert loss_1 < loss_5 < loss_10 def test_frequency_increases_loss(self): model = OkumuraHataModel() loss_450 = model.calculate(make_input(frequency_mhz=450)).path_loss_db loss_900 = model.calculate(make_input(frequency_mhz=900)).path_loss_db assert loss_900 > loss_450 def test_higher_tx_reduces_loss(self): model = OkumuraHataModel() loss_low = model.calculate(make_input(tx_height_m=10)).path_loss_db loss_high = model.calculate(make_input(tx_height_m=50)).path_loss_db assert loss_high < loss_low def test_valid_frequency_range(self): model = OkumuraHataModel() assert model.is_valid_for(make_input(frequency_mhz=150)) assert model.is_valid_for(make_input(frequency_mhz=1500)) assert not model.is_valid_for(make_input(frequency_mhz=2000)) def test_valid_distance_range(self): model = OkumuraHataModel() assert model.is_valid_for(make_input(distance_m=500)) assert model.is_valid_for(make_input(distance_m=20000)) # Out of range assert not model.is_valid_for(make_input(distance_m=50)) def test_model_name(self): model = OkumuraHataModel() assert model.name == "Okumura-Hata" def test_open_environment(self): """Open environment should have even less loss than rural.""" model = OkumuraHataModel() rural = model.calculate(make_input(environment="rural")).path_loss_db open_area = model.calculate(make_input(environment="open")).path_loss_db assert open_area < rural if __name__ == "__main__": instance = TestOkumuraHata() 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.")